request-log-analyzer 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/README.rdoc +4 -3
  2. data/bin/request-log-analyzer +4 -5
  3. data/lib/cli/command_line_arguments.rb +2 -2
  4. data/lib/request_log_analyzer.rb +28 -6
  5. data/lib/request_log_analyzer/{aggregator/base.rb → aggregator.rb} +5 -1
  6. data/lib/request_log_analyzer/aggregator/summarizer.rb +2 -3
  7. data/lib/request_log_analyzer/controller.rb +11 -16
  8. data/lib/request_log_analyzer/file_format.rb +71 -38
  9. data/lib/request_log_analyzer/file_format/merb.rb +32 -26
  10. data/lib/request_log_analyzer/file_format/rails.rb +73 -71
  11. data/lib/request_log_analyzer/file_format/rails_development.rb +93 -95
  12. data/lib/request_log_analyzer/filter.rb +38 -0
  13. data/lib/request_log_analyzer/filter/anonimize.rb +1 -1
  14. data/lib/request_log_analyzer/line_definition.rb +1 -1
  15. data/lib/request_log_analyzer/output.rb +6 -8
  16. data/lib/request_log_analyzer/output/fixed_width.rb +133 -117
  17. data/lib/request_log_analyzer/output/html.rb +138 -60
  18. data/lib/request_log_analyzer/request.rb +3 -1
  19. data/lib/request_log_analyzer/{source/base.rb → source.rb} +5 -0
  20. data/lib/request_log_analyzer/source/{log_file.rb → log_parser.rb} +15 -6
  21. data/lib/request_log_analyzer/tracker.rb +58 -0
  22. data/lib/request_log_analyzer/tracker/category.rb +7 -8
  23. data/lib/request_log_analyzer/tracker/duration.rb +15 -12
  24. data/lib/request_log_analyzer/tracker/hourly_spread.rb +8 -8
  25. data/lib/request_log_analyzer/tracker/timespan.rb +10 -10
  26. data/spec/controller_spec.rb +5 -4
  27. data/spec/database_inserter_spec.rb +5 -8
  28. data/spec/file_format_spec.rb +2 -2
  29. data/spec/file_formats/spec_format.rb +2 -1
  30. data/spec/filter_spec.rb +0 -3
  31. data/spec/log_parser_spec.rb +6 -6
  32. data/spec/merb_format_spec.rb +38 -38
  33. data/spec/rails_format_spec.rb +2 -2
  34. data/spec/request_spec.rb +2 -2
  35. data/spec/spec_helper.rb +3 -37
  36. data/tasks/github-gem.rake +2 -1
  37. metadata +7 -8
  38. data/lib/request_log_analyzer/filter/base.rb +0 -32
  39. data/lib/request_log_analyzer/log_parser.rb +0 -173
  40. data/lib/request_log_analyzer/tracker/base.rb +0 -54
@@ -1,5 +1,5 @@
1
1
  module RequestLogAnalyzer::Tracker
2
-
2
+
3
3
  # Determines the average hourly spread of the parsed requests.
4
4
  # This spread is shown in a graph form.
5
5
  #
@@ -26,15 +26,15 @@ module RequestLogAnalyzer::Tracker
26
26
  # 17:00 - 213 hits : ░░░░░
27
27
  # 18:00 - 107 hits : ░░
28
28
  # ................
29
- class HourlySpread < RequestLogAnalyzer::Tracker::Base
29
+ class HourlySpread < Base
30
30
 
31
31
  attr_reader :first, :last, :request_time_graph
32
-
32
+
33
33
  def prepare
34
34
  options[:field] ||= :timestamp
35
35
  @request_time_graph = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
36
36
  end
37
-
37
+
38
38
  def update(request)
39
39
  request = request.attributes
40
40
  timestamp = request[options[:field]]
@@ -43,10 +43,10 @@ module RequestLogAnalyzer::Tracker
43
43
  @first = timestamp if @first.nil? || timestamp < @first
44
44
  @last = timestamp if @last.nil? || timestamp > @last
45
45
  end
46
-
46
+
47
47
  def report(output)
48
48
  output.title("Requests graph - average per day per hour")
49
-
49
+
50
50
  if @request_time_graph == [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
51
51
  output << "None found.\n"
52
52
  return
@@ -56,12 +56,12 @@ module RequestLogAnalyzer::Tracker
56
56
  last_date = DateTime.parse(@last.to_s, '%Y%m%d%H%M%S')
57
57
  days = (@last && @first) ? (last_date - first_date).ceil : 1
58
58
  total_requests = @request_time_graph.inject(0) { |sum, value| sum + value }
59
-
59
+
60
60
  output.table({}, {:align => :right}, {:type => :ratio, :width => :rest, :treshold => 0.15}) do |rows|
61
61
  @request_time_graph.each_with_index do |requests, index|
62
62
  ratio = requests.to_f / total_requests.to_f
63
63
  requests_per_day = requests / days
64
-
64
+
65
65
  rows << ["#{index.to_s.rjust(3)}:00", "#{requests_per_day} hits", ratio]
66
66
  end
67
67
  end
@@ -1,5 +1,5 @@
1
1
  module RequestLogAnalyzer::Tracker
2
-
2
+
3
3
  # Determines the datetime of the first request and the last request
4
4
  # Also determines the amount of days inbetween these.
5
5
  #
@@ -16,30 +16,30 @@ module RequestLogAnalyzer::Tracker
16
16
  # First request: 2008-07-13 06:25:06
17
17
  # Last request: 2008-07-20 06:18:06
18
18
  # Total time analyzed: 7 days
19
- class Timespan < RequestLogAnalyzer::Tracker::Base
19
+ class Timespan < Base
20
20
 
21
21
  attr_reader :first, :last, :request_time_graph
22
-
22
+
23
23
  def prepare
24
24
  options[:field] ||= :timestamp
25
25
  end
26
-
26
+
27
27
  def update(request)
28
28
  timestamp = request[options[:field]]
29
29
 
30
30
  @first = timestamp if @first.nil? || timestamp < @first
31
31
  @last = timestamp if @last.nil? || timestamp > @last
32
32
  end
33
-
33
+
34
34
  def report(output)
35
35
  output.title(options[:title]) if options[:title]
36
-
36
+
37
37
  first_date = DateTime.parse(@first.to_s, '%Y%m%d%H%M%S') rescue nil
38
38
  last_date = DateTime.parse(@last.to_s, '%Y%m%d%H%M%S') rescue nil
39
-
39
+
40
40
  if @last && @first
41
41
  days = (@last && @first) ? (last_date - first_date).ceil : 1
42
-
42
+
43
43
  output.with_style(:cell_separator => false) do
44
44
  output.table({:width => 20}, {}) do |rows|
45
45
  rows << ['First request:', first_date.strftime('%Y-%m-%d %H:%M:%I')]
@@ -48,7 +48,7 @@ module RequestLogAnalyzer::Tracker
48
48
  end
49
49
  end
50
50
  end
51
-
51
+
52
52
  end
53
53
  end
54
- end
54
+ end
@@ -12,22 +12,23 @@ describe RequestLogAnalyzer::Controller do
12
12
  it "should call the aggregators when run" do
13
13
 
14
14
  mock_output = mock('output')
15
+ mock_output.stub!(:io).and_return($stdout)
15
16
  mock_output.should_receive(:header)
16
- mock_output.should_receive(:footer)
17
+ mock_output.should_receive(:footer)
17
18
 
18
19
  file_format = RequestLogAnalyzer::FileFormat.load(:rails)
19
- source = RequestLogAnalyzer::Source::LogFile.new(file_format, :source_files => log_fixture(:rails_1x))
20
+ source = RequestLogAnalyzer::Source::LogParser.new(file_format, :source_files => log_fixture(:rails_1x))
20
21
  controller = RequestLogAnalyzer::Controller.new(source, :output => mock_output)
21
22
 
22
23
  mock_aggregator = mock('aggregator')
23
24
  mock_aggregator.should_receive(:prepare).once.ordered
24
- mock_aggregator.should_receive(:aggregate).with(an_instance_of(RequestLogAnalyzer::Request)).at_least(:twice).ordered
25
+ mock_aggregator.should_receive(:aggregate).with(an_instance_of(file_format.class::Request)).at_least(:twice).ordered
25
26
  mock_aggregator.should_receive(:finalize).once.ordered
26
27
  mock_aggregator.should_receive(:report).once.ordered
27
28
 
28
29
  another_mock_aggregator = mock('another aggregator')
29
30
  another_mock_aggregator.should_receive(:prepare).once.ordered
30
- another_mock_aggregator.should_receive(:aggregate).with(an_instance_of(RequestLogAnalyzer::Request)).at_least(:twice).ordered
31
+ another_mock_aggregator.should_receive(:aggregate).with(an_instance_of(file_format.class::Request)).at_least(:twice).ordered
31
32
  another_mock_aggregator.should_receive(:finalize).once.ordered
32
33
  another_mock_aggregator.should_receive(:report).once.ordered
33
34
 
@@ -8,7 +8,7 @@ describe RequestLogAnalyzer::Aggregator::Database, "schema creation" do
8
8
  include RequestLogAnalyzerSpecHelper
9
9
 
10
10
  before(:each) do
11
- log_parser = RequestLogAnalyzer::LogParser.new(spec_format)
11
+ log_parser = RequestLogAnalyzer::Source::LogParser.new(spec_format)
12
12
  @database_inserter = RequestLogAnalyzer::Aggregator::Database.new(log_parser, :database => TEST_DATABASE_FILE)
13
13
  end
14
14
 
@@ -68,16 +68,13 @@ describe RequestLogAnalyzer::Aggregator::Database, "record insertion" do
68
68
  include RequestLogAnalyzerSpecHelper
69
69
 
70
70
  before(:each) do
71
- log_parser = RequestLogAnalyzer::LogParser.new(spec_format)
71
+ log_parser = RequestLogAnalyzer::Source::LogParser.new(spec_format)
72
72
  @database_inserter = RequestLogAnalyzer::Aggregator::Database.new(log_parser, :database => TEST_DATABASE_FILE)
73
73
  @database_inserter.prepare
74
74
 
75
- @incomplete_request = RequestLogAnalyzer::Request.create(spec_format, {:line_type => :first, :request_no => 564})
76
- @completed_request = RequestLogAnalyzer::Request.create(spec_format,
77
- {:line_type => :first, :request_no => 564},
78
- {:line_type => :test, :test_capture => "awesome"},
79
- {:line_type => :test, :test_capture => "indeed"},
80
- {:line_type => :last, :request_no => 564})
75
+ @incomplete_request = spec_format.create_request( {:line_type => :first, :request_no => 564})
76
+ @completed_request = spec_format.create_request( {:line_type => :first, :request_no => 564}, {:line_type => :test, :test_capture => "awesome"},
77
+ {:line_type => :test, :test_capture => "indeed"}, {:line_type => :last, :request_no => 564})
81
78
  end
82
79
 
83
80
  after(:each) do
@@ -3,8 +3,8 @@ require File.dirname(__FILE__) + '/spec_helper'
3
3
  describe RequestLogAnalyzer::FileFormat, :format_definition do
4
4
 
5
5
  before(:each) do
6
- @first_file_format = Class.new(RequestLogAnalyzer::FileFormat)
7
- @second_file_format = Class.new(RequestLogAnalyzer::FileFormat)
6
+ @first_file_format = Class.new(RequestLogAnalyzer::FileFormat::Base)
7
+ @second_file_format = Class.new(RequestLogAnalyzer::FileFormat::Base)
8
8
  end
9
9
 
10
10
  it "should specify lines with a hash" do
@@ -1,4 +1,4 @@
1
- class SpecFormat < RequestLogAnalyzer::FileFormat
1
+ class SpecFormat < RequestLogAnalyzer::FileFormat::Base
2
2
 
3
3
  format_definition.first do |line|
4
4
  line.header = true
@@ -23,4 +23,5 @@ class SpecFormat < RequestLogAnalyzer::FileFormat
23
23
  report do |analyze|
24
24
  analyze.category :test_capture, :title => 'What is testing exactly?'
25
25
  end
26
+
26
27
  end
@@ -1,7 +1,4 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
- require File.dirname(__FILE__) + '/../lib/request_log_analyzer/filter/timespan'
3
- require File.dirname(__FILE__) + '/../lib/request_log_analyzer/filter/field'
4
- require File.dirname(__FILE__) + '/../lib/request_log_analyzer/filter/anonimize'
5
2
 
6
3
  describe RequestLogAnalyzer::Filter::Timespan, 'both before and after' do
7
4
  include RequestLogAnalyzerSpecHelper
@@ -1,10 +1,10 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
- describe RequestLogAnalyzer::LogParser, :requests do
3
+ describe RequestLogAnalyzer::Source::LogParser, :requests do
4
4
  include RequestLogAnalyzerSpecHelper
5
5
 
6
6
  before(:each) do
7
- @log_parser = RequestLogAnalyzer::LogParser.new(spec_format)
7
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(spec_format)
8
8
  end
9
9
 
10
10
  it "should have multiple line definitions" do
@@ -16,13 +16,13 @@ describe RequestLogAnalyzer::LogParser, :requests do
16
16
  end
17
17
 
18
18
  it "should parse more lines than requests" do
19
- @log_parser.should_receive(:handle_request).with(an_instance_of(RequestLogAnalyzer::Request)).twice
19
+ @log_parser.should_receive(:handle_request).with(an_instance_of(SpecFormat::Request)).twice
20
20
  @log_parser.parse_file(log_fixture(:test_language_combined))
21
21
  @log_parser.parsed_lines.should > 2
22
22
  end
23
23
 
24
24
  it "should parse requests spanned over multiple files" do
25
- @log_parser.should_receive(:handle_request).with(an_instance_of(RequestLogAnalyzer::Request)).once
25
+ @log_parser.should_receive(:handle_request).with(an_instance_of(SpecFormat::Request)).once
26
26
  @log_parser.parse_files([log_fixture(:multiple_files_1), log_fixture(:multiple_files_2)])
27
27
  end
28
28
 
@@ -47,11 +47,11 @@ describe RequestLogAnalyzer::LogParser, :requests do
47
47
 
48
48
  end
49
49
 
50
- describe RequestLogAnalyzer::LogParser, :warnings do
50
+ describe RequestLogAnalyzer::Source::LogParser, :warnings do
51
51
  include RequestLogAnalyzerSpecHelper
52
52
 
53
53
  before(:each) do
54
- @log_parser = RequestLogAnalyzer::LogParser.new(spec_format)
54
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(spec_format)
55
55
  end
56
56
 
57
57
  it "should warn about teaser matching problems" do
@@ -1,38 +1,38 @@
1
- # require File.dirname(__FILE__) + '/spec_helper'
2
- #
3
- # describe RequestLogAnalyzer::LogParser, "Merb" do
4
- # include RequestLogAnalyzerSpecHelper
5
- #
6
- # before(:each) do
7
- # @log_parser = RequestLogAnalyzer::LogParser.new(:merb)
8
- # end
9
- #
10
- # it "should have a valid language definitions" do
11
- # @log_parser.file_format.should be_valid
12
- # end
13
- #
14
- # it "should parse a stream and find valid requests" do
15
- # File.open(log_fixture(:merb), 'r') do |io|
16
- # @log_parser.parse_io(io) do |request|
17
- # request.should be_kind_of(RequestLogAnalyzer::Request)
18
- # end
19
- # end
20
- # end
21
- #
22
- # it "should find 11 completed requests" do
23
- # @log_parser.should_receive(:handle_request).exactly(11).times
24
- # @log_parser.parse_file(log_fixture(:merb))
25
- # end
26
- #
27
- # it "should parse all details from a request correctly" do
28
- # request = nil
29
- # @log_parser.parse_file(log_fixture(:merb)) { |found_request| request ||= found_request }
30
- #
31
- # request.should be_completed
32
- # request[:timestamp].should == DateTime.parse('Fri Aug 29 11:10:23 +0200 2008')
33
- # request[:dispatch_time].should == 0.243424
34
- # request[:after_filters_time].should == 6.9e-05
35
- # request[:before_filters_time].should == 0.213213
36
- # request[:action_time].should == 0.241652
37
- # end
38
- # end
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Source::LogParser, :merb do
4
+ include RequestLogAnalyzerSpecHelper
5
+
6
+ before(:each) do
7
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(RequestLogAnalyzer::FileFormat.load(:merb))
8
+ end
9
+
10
+ it "should have a valid language definitions" do
11
+ @log_parser.file_format.should be_valid
12
+ end
13
+
14
+ it "should parse a stream and find valid requests" do
15
+ File.open(log_fixture(:merb), 'r') do |io|
16
+ @log_parser.parse_io(io) do |request|
17
+ request.should be_kind_of(RequestLogAnalyzer::Request)
18
+ end
19
+ end
20
+ end
21
+
22
+ it "should find 11 completed requests" do
23
+ @log_parser.should_receive(:handle_request).exactly(11).times
24
+ @log_parser.parse_file(log_fixture(:merb))
25
+ end
26
+
27
+ it "should parse all details from a request correctly" do
28
+ request = nil
29
+ @log_parser.parse_file(log_fixture(:merb)) { |found_request| request ||= found_request }
30
+
31
+ request.should be_completed
32
+ #request[:timestamp].should == DateTime.parse('Fri Aug 29 11:10:23 +0200 2008') # FIX ME
33
+ request[:dispatch_time].should == 0.243424
34
+ request[:after_filters_time].should == 6.9e-05
35
+ request[:before_filters_time].should == 0.213213
36
+ request[:action_time].should == 0.241652
37
+ end
38
+ end
@@ -1,10 +1,10 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
- describe RequestLogAnalyzer::LogParser, "Rails" do
3
+ describe RequestLogAnalyzer::Source::LogParser, "Rails" do
4
4
  include RequestLogAnalyzerSpecHelper
5
5
 
6
6
  before(:each) do
7
- @log_parser = RequestLogAnalyzer::LogParser.new(RequestLogAnalyzer::FileFormat.load(:rails))
7
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(RequestLogAnalyzer::FileFormat.load(:rails))
8
8
  end
9
9
 
10
10
  it "should have a valid language definitions" do
@@ -5,7 +5,7 @@ describe RequestLogAnalyzer::Request, :incomplete_request do
5
5
  include RequestLogAnalyzerSpecHelper
6
6
 
7
7
  before(:each) do
8
- @incomplete_request = RequestLogAnalyzer::Request.new(spec_format)
8
+ @incomplete_request = spec_format.create_request
9
9
  @incomplete_request << { :line_type => :test, :lineno => 1, :test_capture => 'awesome!' }
10
10
  end
11
11
 
@@ -37,7 +37,7 @@ describe RequestLogAnalyzer::Request, :completed_request do
37
37
  include RequestLogAnalyzerSpecHelper
38
38
 
39
39
  before(:each) do
40
- @completed_request = RequestLogAnalyzer::Request.new(spec_format)
40
+ @completed_request = spec_format.create_request
41
41
  @completed_request << { :line_type => :first, :lineno => 1, :name => 'first line!' }
42
42
  @completed_request << { :line_type => :test, :lineno => 4, :test_capture => 'testing' }
43
43
  @completed_request << { :line_type => :test, :lineno => 7, :test_capture => 'testing some more' }
@@ -21,47 +21,13 @@ module RequestLogAnalyzerSpecHelper
21
21
  File.dirname(__FILE__) + "/fixtures/#{name}.log"
22
22
  end
23
23
 
24
- def request(fields, format = TestFileFormat)
24
+ def request(fields, format = spec_format)
25
25
  if fields.kind_of?(Array)
26
- RequestLogAnalyzer::Request.create(format, *fields)
26
+ format.create_request(*fields)
27
27
  else
28
- RequestLogAnalyzer::Request.create(format, fields)
28
+ format.create_request(fields)
29
29
  end
30
30
  end
31
31
 
32
32
  end
33
33
 
34
- module TestFileFormat
35
-
36
- module Summarizer
37
- def self.included(base)
38
- # monkey patching for summarizer here :-)
39
- end
40
- end
41
-
42
- module LogParser
43
- def self.included(base)
44
- # monkey patching for log parser here :-)
45
- end
46
- end
47
-
48
- LINE_DEFINITIONS = {
49
- :first => {
50
- :header => true,
51
- :teaser => /processing /,
52
- :regexp => /processing request (\d+)/,
53
- :captures => [{ :name => :request_no, :type => :integer, :anonymize => :slightly }]
54
- },
55
- :test => {
56
- :teaser => /testing /,
57
- :regexp => /testing is (\w+)/,
58
- :captures => [{ :name => :test_capture, :type => :string, :anonymize => true}]
59
- },
60
- :last => {
61
- :footer => true,
62
- :teaser => /finishing /,
63
- :regexp => /finishing request (\d+)/,
64
- :captures => [{ :name => :request_no, :type => :integer}]
65
- }
66
- }
67
- end
@@ -241,7 +241,8 @@ module Rake
241
241
 
242
242
  def release_task
243
243
  puts
244
- puts "Released #{@name} - version#{@specification.version}"
244
+ puts '------------------------------------------------------------'
245
+ puts "Released #{@name} - version #{@specification.version}"
245
246
  end
246
247
  end
247
248
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: request-log-analyzer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willem van Bergen
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-01-18 00:00:00 +01:00
13
+ date: 2009-01-21 00:00:00 +01:00
14
14
  default_executable: request-log-analyzer
15
15
  dependencies: []
16
16
 
@@ -37,7 +37,7 @@ files:
37
37
  - lib/request_log_analyzer
38
38
  - lib/request_log_analyzer.rb
39
39
  - lib/request_log_analyzer/aggregator
40
- - lib/request_log_analyzer/aggregator/base.rb
40
+ - lib/request_log_analyzer/aggregator.rb
41
41
  - lib/request_log_analyzer/aggregator/database.rb
42
42
  - lib/request_log_analyzer/aggregator/echo.rb
43
43
  - lib/request_log_analyzer/aggregator/summarizer.rb
@@ -48,12 +48,11 @@ files:
48
48
  - lib/request_log_analyzer/file_format/rails.rb
49
49
  - lib/request_log_analyzer/file_format/rails_development.rb
50
50
  - lib/request_log_analyzer/filter
51
+ - lib/request_log_analyzer/filter.rb
51
52
  - lib/request_log_analyzer/filter/anonimize.rb
52
- - lib/request_log_analyzer/filter/base.rb
53
53
  - lib/request_log_analyzer/filter/field.rb
54
54
  - lib/request_log_analyzer/filter/timespan.rb
55
55
  - lib/request_log_analyzer/line_definition.rb
56
- - lib/request_log_analyzer/log_parser.rb
57
56
  - lib/request_log_analyzer/log_processor.rb
58
57
  - lib/request_log_analyzer/output
59
58
  - lib/request_log_analyzer/output.rb
@@ -61,10 +60,10 @@ files:
61
60
  - lib/request_log_analyzer/output/html.rb
62
61
  - lib/request_log_analyzer/request.rb
63
62
  - lib/request_log_analyzer/source
64
- - lib/request_log_analyzer/source/base.rb
65
- - lib/request_log_analyzer/source/log_file.rb
63
+ - lib/request_log_analyzer/source.rb
64
+ - lib/request_log_analyzer/source/log_parser.rb
66
65
  - lib/request_log_analyzer/tracker
67
- - lib/request_log_analyzer/tracker/base.rb
66
+ - lib/request_log_analyzer/tracker.rb
68
67
  - lib/request_log_analyzer/tracker/category.rb
69
68
  - lib/request_log_analyzer/tracker/duration.rb
70
69
  - lib/request_log_analyzer/tracker/hourly_spread.rb