nbogie-production_log_analyzer 1.5.1.1 → 1.5.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/History.txt +4 -0
- data/README.txt +27 -0
- data/Rakefile +1 -1
- data/test/test_report_differ.rb +178 -0
- data/test/test_report_parser.rb +140 -0
- metadata +22 -24
data/.gemtest
ADDED
File without changes
|
data/History.txt
CHANGED
data/README.txt
CHANGED
@@ -138,6 +138,33 @@ In the future, pl_analyze will be able to read from STDIN.
|
|
138
138
|
TeamsController#progress took 0.000s
|
139
139
|
TeamsController#progress took 0.000s
|
140
140
|
|
141
|
+
There's an experimental report-differ which you can invoke as follows:
|
142
|
+
|
143
|
+
ruby -Ilib report_differ.rb report_b.txt report_a.txt > report_diff.txt
|
144
|
+
|
145
|
+
Sample diff output
|
146
|
+
|
147
|
+
Request_Times_Summary: Count Avg Max
|
148
|
+
ALL_REQUESTS +1.4(16515->22909) -2.2(0.405->0.18) -1.6(59.678->37.721)
|
149
|
+
|
150
|
+
SleepwalkersController#update.PUT.xml +1.4(6249->8793) -1.1(0.079->0.07) -2.7(8.328->3.103)
|
151
|
+
SleepwalkersController#update.PUT.csv +1.4(6245->8807) -1.0(0.099->0.097) +1.2(2.536->3.02)
|
152
|
+
SleepwalkersController#show.GET +1.4(509->710) -1.2(0.072->0.061) -2.3(3.505->1.504)
|
153
|
+
SleepwalkersController#show.GET.json +1.4(496->702) -1.2(0.076->0.064) -7.3(4.874->0.663)
|
154
|
+
AccomplicesController#history.GET.csv +1.4(470->680) -3.3(5.35->1.618) -1.5(20.681->13.692)
|
155
|
+
WindsController#feeds.GET +1.4(237->320) -1.1(0.201->0.183) +1.5(1.304->1.989)
|
156
|
+
ScamsController#show.GET +1.3(236->317) -3.0(11.793->3.869) -1.6(59.678->37.721)
|
157
|
+
SleepwalkersController#update.PUT -1.1(9->8) -1.1(0.051->0.047) -1.2(0.09->0.076)
|
158
|
+
AccomplicesController#show.GET.xml 1.0(2->2) -1.0(0.102->0.1) -1.0(0.184->0.179)
|
159
|
+
ScamsController#map.GET +6.0(2->12) -3.5(1.075->0.305) +1.1(2.008->2.112)
|
160
|
+
WindsController#feeds.GET.atom 1.0(1->1) -1.1(0.061->0.058) -1.1(0.061->0.058)
|
161
|
+
ScamsController#tag.GET.rss 1.0(1->1) -1.0(0.134->0.129) -1.0(0.134->0.129)
|
162
|
+
|
163
|
+
Unmatched_Actions:
|
164
|
+
|
165
|
+
ScamsController#index.GET.json only in report_a.txt
|
166
|
+
Api::V1::MisinterpretationsController#destroy.DELETE. only in report_a.txt
|
167
|
+
|
141
168
|
== What's missing
|
142
169
|
|
143
170
|
* More reports
|
data/Rakefile
CHANGED
@@ -8,7 +8,7 @@ Hoe.plugin :email, :perforce # not on minitest yet
|
|
8
8
|
Hoe.spec 'nbogie-production_log_analyzer' do
|
9
9
|
developer 'Eric Hodel', 'drbrain@segment7.net'
|
10
10
|
self.name = 'nbogie-production_log_analyzer'
|
11
|
-
self.version = '1.5.1.
|
11
|
+
self.version = '1.5.1.2'
|
12
12
|
|
13
13
|
extra_deps << ['rails_analyzer_tools', '>= 1.4.0']
|
14
14
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
require 'report/report_parser'
|
6
|
+
require 'report/report_differ'
|
7
|
+
|
8
|
+
class TestReportDiffer < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def file_data(filename)
|
11
|
+
'test/example_data/' + filename
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@differ = ReportDiffer.new
|
16
|
+
report_filenames = ['report_a', 'report_b'].map do |keyname|
|
17
|
+
"test/example_data/#{keyname}.txt"
|
18
|
+
end
|
19
|
+
create_reports()
|
20
|
+
#ReportDiffer.compare_files(report_filenames[0], report_filenames[1])
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def test_initialization
|
25
|
+
differ = ReportDiffer.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_compute_difference_in_values()
|
29
|
+
data = [
|
30
|
+
#when increasing
|
31
|
+
[100, 1000, 10],
|
32
|
+
[100, 200, 2],
|
33
|
+
[100, 130, 1.3],
|
34
|
+
#when same
|
35
|
+
[100, 100, 1],
|
36
|
+
[0,0, 1],
|
37
|
+
#when lessening
|
38
|
+
[100, 90, -1.11],
|
39
|
+
[100, 50, -2],
|
40
|
+
[100, 10, -10],
|
41
|
+
#when one number is zero - not sure what we should return here.
|
42
|
+
[0.00, 1, 999999],
|
43
|
+
[1, 0.00, -999999],
|
44
|
+
]
|
45
|
+
data.each do |group|
|
46
|
+
va, vb, expected = group
|
47
|
+
actual = @differ.compute_difference_in_values(va, vb)
|
48
|
+
assert_equal(expected, actual, "for #{va}->#{vb}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def test_compare
|
54
|
+
threshold = 1.2
|
55
|
+
actual=@differ.compare(@report_a, @report_b, threshold)
|
56
|
+
assert_not_nil actual
|
57
|
+
expected = {
|
58
|
+
:summary_diff=>
|
59
|
+
{
|
60
|
+
:diff_count=>{:change => -1.11, :from => 100, :to => 90},
|
61
|
+
:diff_avg_time=>{:change => 1.5, :from => 10, :to => 15},
|
62
|
+
:diff_min_time=>{:change => 1, :from => 0.1, :to => 0.1},
|
63
|
+
:diff_max_time=>{:change => 2.0, :from => 20.0, :to => 40.0},
|
64
|
+
:diff_std_dev=>{:change => 3.0, :from => 2.0, :to => 6.0}},
|
65
|
+
:request_diffs=>
|
66
|
+
{"MyCon#MyAc.get.xml"=>
|
67
|
+
{
|
68
|
+
:diff_count=>{:change => 1.1, :from => 100, :to => 110},
|
69
|
+
:diff_avg_time=>{:change => 1.2, :from => 100, :to => 120},
|
70
|
+
:diff_std_dev=>{:change => 1.3, :from => 100, :to => 130},
|
71
|
+
:diff_min_time=>{:change => 1.4, :from => 100, :to => 140},
|
72
|
+
:diff_max_time=>{:change => 1.5, :from => 100, :to => 150} }},
|
73
|
+
:unmatched_actions => [],
|
74
|
+
:threshold => threshold
|
75
|
+
}
|
76
|
+
|
77
|
+
|
78
|
+
assert_equal(expected, actual)
|
79
|
+
|
80
|
+
ra1 = Request.create('MyCon','MyAc2',600,1,1,1,1)
|
81
|
+
rb1 = Request.create('MyCon','MyAc2',610,1,1,1,1)
|
82
|
+
ra2 = Request.create('MyCon','MyAc3',50,1,1,1,1)
|
83
|
+
rb2 = Request.create('MyCon','MyAc3',50,1,1,1,1)
|
84
|
+
@report_a.requests << ra1 << ra2
|
85
|
+
@report_b.requests << rb1 << rb2
|
86
|
+
assert_equal 3, @report_a.requests.size
|
87
|
+
|
88
|
+
actual=@differ.compare(@report_a, @report_b, threshold)
|
89
|
+
end
|
90
|
+
def test_prepare_report()
|
91
|
+
diff_data = @differ.compare(@report_a, @report_b)
|
92
|
+
report_text = @differ.prepare_report(diff_data)
|
93
|
+
#TODO: check report text as as we expect
|
94
|
+
##puts report_text
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_matched_requests_values_ab
|
98
|
+
matches = @differ.find_matched_actions(@report_a, @report_b)
|
99
|
+
assert_equal 1, matches.size
|
100
|
+
assert_equal [100, 110], matches.first.values_ab(:count)
|
101
|
+
assert_equal [100, 120], matches.first.values_ab(:avg_time)
|
102
|
+
assert_equal [100, 130], matches.first.values_ab(:std_dev)
|
103
|
+
assert_equal [100, 140], matches.first.values_ab(:min_time)
|
104
|
+
assert_equal [100, 150], matches.first.values_ab(:max_time)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_find_matched_actions
|
108
|
+
matches = @differ.find_matched_actions(@report_a, @report_b)
|
109
|
+
assert_equal 1, matches.size
|
110
|
+
the_match=matches.first
|
111
|
+
assert_equal [@report_a.requests.first, @report_b.requests.first], [the_match.request1, the_match.request2]
|
112
|
+
|
113
|
+
#with one of the reports having NO requests
|
114
|
+
@report_a.requests.clear
|
115
|
+
assert_equal 0, @differ.find_matched_actions(@report_a, @report_b).size
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_find_unmatched_actions
|
119
|
+
#initially, should return empty list - all match
|
120
|
+
assert_equal [], @differ.find_unmatched_actions(@report_a, @report_b)
|
121
|
+
#add a request to only one report - should find it
|
122
|
+
odd_req = Request.create('MyCon','UniqueAction',110,120,130,140,150)
|
123
|
+
@report_a.requests << odd_req
|
124
|
+
unmatcheds = @differ.find_unmatched_actions(@report_a, @report_b)
|
125
|
+
assert_equal 1, unmatcheds.size
|
126
|
+
assert_equal 'UniqueAction', unmatcheds.first.request.action
|
127
|
+
assert_equal @report_a, unmatcheds.first.report
|
128
|
+
|
129
|
+
#add another request (only to rep b)
|
130
|
+
odd_req2 = Request.create('UniqueCon','MyAc',110,120,130,140,150)
|
131
|
+
@report_b.requests << odd_req2
|
132
|
+
unmatcheds = @differ.find_unmatched_actions(@report_a, @report_b)
|
133
|
+
assert_equal 2, unmatcheds.size
|
134
|
+
assert_equal 'UniqueAction', unmatcheds.first.request.action
|
135
|
+
assert_equal @report_a, unmatcheds.first.report
|
136
|
+
assert_equal 'UniqueCon', unmatcheds.last.request.controller
|
137
|
+
assert_equal @report_b, unmatcheds.last.report
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_compare_summaries
|
141
|
+
actual=@differ.compare_summaries(@report_a, @report_b)
|
142
|
+
assert_equal(
|
143
|
+
{
|
144
|
+
:diff_count=>{:change => -1.11, :from => 100, :to => 90},
|
145
|
+
:diff_avg_time=>{:change => 1.5, :from => 10, :to => 15},
|
146
|
+
:diff_std_dev=>{:change => 3.0, :from => 2.0, :to => 6.0},
|
147
|
+
:diff_min_time=>{:change => 1, :from => 0.1, :to => 0.1},
|
148
|
+
:diff_max_time=>{:change => 2.0, :from => 20.0, :to => 40.0},
|
149
|
+
}, actual)
|
150
|
+
end
|
151
|
+
|
152
|
+
#populate @report_a, @report_b
|
153
|
+
def create_reports
|
154
|
+
ra = Report.new
|
155
|
+
ra.summary.count=100
|
156
|
+
ra.summary.avg_time=10.0
|
157
|
+
ra.summary.std_dev=2.0
|
158
|
+
ra.summary.min_time=0.1
|
159
|
+
ra.summary.max_time=20.0
|
160
|
+
|
161
|
+
ca1 = Request.create('MyCon','MyAc',100,100,100,100,100, {:verb=>'get',:format =>'xml'})
|
162
|
+
cb1 = Request.create('MyCon','MyAc',110,120,130,140,150, {:verb=>'get',:format =>'xml'})
|
163
|
+
ra.requests << ca1
|
164
|
+
|
165
|
+
rb = Report.new
|
166
|
+
rb.summary.count=90
|
167
|
+
rb.summary.avg_time=15.0
|
168
|
+
rb.summary.std_dev=6
|
169
|
+
rb.summary.min_time=0.1
|
170
|
+
rb.summary.max_time=40.0
|
171
|
+
rb.requests << cb1
|
172
|
+
@report_a = ra
|
173
|
+
@report_b = rb
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
end
|
178
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
require 'report/report_parser'
|
6
|
+
|
7
|
+
class TestReportParser < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def file_data(filename)
|
10
|
+
'test/example_data/' + filename
|
11
|
+
end
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@parser = ReportParser.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_initialization
|
18
|
+
parser = ReportParser.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_can_parse_report
|
22
|
+
content = File.read(file_data('example.txt'))
|
23
|
+
|
24
|
+
result = @parser.parse("my report name", content)
|
25
|
+
assert_not_nil(result)
|
26
|
+
assert_equal("my report name", result.name)
|
27
|
+
assert_equal(8, result.requests.size)
|
28
|
+
expected_controllers= %w(
|
29
|
+
FooController
|
30
|
+
FooController
|
31
|
+
FooController
|
32
|
+
FooController
|
33
|
+
BarController
|
34
|
+
HealthController
|
35
|
+
QuxController
|
36
|
+
UsersController)
|
37
|
+
assert_equal(expected_controllers, result.requests.map{|rq| rq.controller})
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_can_extract_from_all_requests
|
41
|
+
content = File.read(file_data('example.txt'))
|
42
|
+
result = @parser.parse("whatever", content)
|
43
|
+
assert_equal(11830,result.summary.count)
|
44
|
+
assert_equal(0.185,result.summary.avg_time)
|
45
|
+
assert_equal(0.212,result.summary.std_dev)
|
46
|
+
assert_equal(0.001,result.summary.min_time)
|
47
|
+
assert_equal(3.553,result.summary.max_time)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_can_parse_a_request
|
51
|
+
line = "ApiFooController#update.PUT.xml: 3557 0.144 0.137 0.059 3.212"
|
52
|
+
request = @parser.parse_request(line)
|
53
|
+
assert_equal("ApiFooController",request.controller)
|
54
|
+
assert_equal("update",request.action)
|
55
|
+
assert_equal("PUT",request.verb)
|
56
|
+
assert_equal("xml",request.format)
|
57
|
+
assert_equal(3557,request.count)
|
58
|
+
assert_equal(0.144,request.avg_time)
|
59
|
+
assert_equal(0.137,request.std_dev)
|
60
|
+
assert_equal(0.059,request.min_time)
|
61
|
+
assert_equal(3.212,request.max_time)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_should_normalize_request_format_to_lowercase
|
65
|
+
line = "ApiFooController#update.PUT.XmL: 3557 0.144 0.137 0.059 3.212"
|
66
|
+
request = @parser.parse_request(line)
|
67
|
+
assert_equal("xml",request.format)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_should_normalize_request_verb_to_uppercase
|
71
|
+
line = "ApiFooController#update.puT.xml: 3557 0.144 0.137 0.059 3.212"
|
72
|
+
request = @parser.parse_request(line)
|
73
|
+
assert_equal("PUT",request.verb)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_should_parse_request_having_no_format
|
77
|
+
line = "ApiFooController#update.GET: 3557 0.144 0.137 0.059 3.212"
|
78
|
+
request = @parser.parse_request(line)
|
79
|
+
assert_equal("GET",request.verb)
|
80
|
+
assert_nil(request.format)
|
81
|
+
assert_equal(0.144,request.avg_time)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_can_parse_a_request_having_module_named_controller_name
|
85
|
+
line = "Api::V1::FooController#show.GET: 11707 0.107 0.263 0.015 19.075"
|
86
|
+
request = @parser.parse_request(line)
|
87
|
+
assert_equal("Api::V1::FooController",request.controller)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_should_parse_a_few_lines
|
91
|
+
should_parse "QuxController#index.GET.rss: 5 0.241 0.078 0.121 0.367"
|
92
|
+
end
|
93
|
+
|
94
|
+
def should_parse(line)
|
95
|
+
@parser.parse_request(line)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
class TestRequest < Test::Unit::TestCase
|
101
|
+
def test_remembers_its_values
|
102
|
+
r = Request.new
|
103
|
+
r.controller = "somecontroller"
|
104
|
+
r.action = "someaction"
|
105
|
+
r.verb = "GET"
|
106
|
+
r.format = "xml"
|
107
|
+
r.count = 100
|
108
|
+
r.avg_time = 1.123
|
109
|
+
r.std_dev = 0.22
|
110
|
+
r.min_time = 3.33
|
111
|
+
r.max_time = 10.10
|
112
|
+
assert_equal("somecontroller",r.controller)
|
113
|
+
assert_equal("someaction",r.action)
|
114
|
+
assert_equal("GET",r.verb)
|
115
|
+
assert_equal("xml",r.format)
|
116
|
+
assert_equal(3.33,r.min_time)
|
117
|
+
assert_equal(10.10,r.max_time)
|
118
|
+
assert_equal(1.123,r.avg_time)
|
119
|
+
assert_equal(0.22,r.std_dev)
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_create
|
123
|
+
req = Request.create('Con','Ac',1,2,3,4,5, {:verb=>'get',:format =>'xml'})
|
124
|
+
assert_equal('Con', req.controller)
|
125
|
+
assert_equal('Ac', req.action)
|
126
|
+
assert_equal(1, req.count)
|
127
|
+
assert_equal(2, req.avg_time)
|
128
|
+
assert_equal(3, req.std_dev)
|
129
|
+
assert_equal(4, req.min_time)
|
130
|
+
assert_equal(5, req.max_time)
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_should_not_fixup_already_fixed_up_name
|
134
|
+
r = Request.new
|
135
|
+
r.controller = "Api::V1::GraultController"
|
136
|
+
r.fixup_controller_name!
|
137
|
+
assert_equal("Api::V1::GraultController",r.controller)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nbogie-production_log_analyzer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 119
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 1
|
7
8
|
- 5
|
8
9
|
- 1
|
9
|
-
-
|
10
|
-
version: 1.5.1.
|
10
|
+
- 2
|
11
|
+
version: 1.5.1.2
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- Eric Hodel
|
@@ -15,16 +16,17 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date:
|
19
|
-
default_executable:
|
19
|
+
date: 2011-07-07 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: rails_analyzer_tools
|
23
23
|
prerelease: false
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
25
26
|
requirements:
|
26
27
|
- - ">="
|
27
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 7
|
28
30
|
segments:
|
29
31
|
- 1
|
30
32
|
- 4
|
@@ -33,33 +35,21 @@ dependencies:
|
|
33
35
|
type: :runtime
|
34
36
|
version_requirements: *id001
|
35
37
|
- !ruby/object:Gem::Dependency
|
36
|
-
name:
|
38
|
+
name: hoe
|
37
39
|
prerelease: false
|
38
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
39
42
|
requirements:
|
40
43
|
- - ">="
|
41
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 35
|
42
46
|
segments:
|
43
47
|
- 2
|
44
|
-
-
|
48
|
+
- 9
|
45
49
|
- 4
|
46
|
-
version: 2.
|
50
|
+
version: 2.9.4
|
47
51
|
type: :development
|
48
52
|
version_requirements: *id002
|
49
|
-
- !ruby/object:Gem::Dependency
|
50
|
-
name: hoe
|
51
|
-
prerelease: false
|
52
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
-
requirements:
|
54
|
-
- - ">="
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
segments:
|
57
|
-
- 2
|
58
|
-
- 6
|
59
|
-
- 0
|
60
|
-
version: 2.6.0
|
61
|
-
type: :development
|
62
|
-
version_requirements: *id003
|
63
53
|
description: |-
|
64
54
|
production_log_analyzer lets you find out which actions on a Rails
|
65
55
|
site are slowing you down.
|
@@ -99,7 +89,9 @@ files:
|
|
99
89
|
- test/test_action_grep.rb
|
100
90
|
- test/test_analyzer.rb
|
101
91
|
- test/test_parser.rb
|
102
|
-
|
92
|
+
- test/test_report_differ.rb
|
93
|
+
- test/test_report_parser.rb
|
94
|
+
- .gemtest
|
103
95
|
homepage: http://seattlerb.rubyforge.org/production_log_analyzer
|
104
96
|
licenses: []
|
105
97
|
|
@@ -110,27 +102,33 @@ rdoc_options:
|
|
110
102
|
require_paths:
|
111
103
|
- lib
|
112
104
|
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
113
106
|
requirements:
|
114
107
|
- - ">="
|
115
108
|
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
116
110
|
segments:
|
117
111
|
- 0
|
118
112
|
version: "0"
|
119
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
120
115
|
requirements:
|
121
116
|
- - ">="
|
122
117
|
- !ruby/object:Gem::Version
|
118
|
+
hash: 3
|
123
119
|
segments:
|
124
120
|
- 0
|
125
121
|
version: "0"
|
126
122
|
requirements: []
|
127
123
|
|
128
124
|
rubyforge_project: nbogie-production_log_analyzer
|
129
|
-
rubygems_version: 1.
|
125
|
+
rubygems_version: 1.8.5
|
130
126
|
signing_key:
|
131
127
|
specification_version: 3
|
132
128
|
summary: production_log_analyzer lets you find out which actions on a Rails site are slowing you down
|
133
129
|
test_files:
|
134
130
|
- test/test_parser.rb
|
135
131
|
- test/test_analyzer.rb
|
132
|
+
- test/test_report_differ.rb
|
136
133
|
- test/test_action_grep.rb
|
134
|
+
- test/test_report_parser.rb
|