Class: RequestLogAnalyzer::Controller

Inherits:
Object
  • Object
show all
Defined in:
lib/request_log_analyzer/controller.rb

Overview

The RequestLogAnalyzer::Controller class creates a LogParser instance for the requested file format, and connect it with sources and aggregators.

Sources are streams or files from which the requests will be parsed. Aggregators will handle every passed request to yield a meaningfull results.

  • Use the build-function to build a controller instance using command line arguments.

  • Use add_aggregator to register a new aggregator

  • Use add_source to register a new aggregator

  • Use the run! method to start the parser and send the requests to the aggregators.

Note that the order of sources can be imported if you have log files than succeed eachother. Requests that span over succeeding files will be parsed correctly if the sources are registered in the correct order. This can be helpful to parse requests from several logrotated log files.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, options = {}) ⇒ Controller

Builds a new Controller for the given log file format. format Logfile format. Defaults to :rails Options are passd on to the LogParser.

  • :aggregator Aggregator array.

  • :database Database the controller should use.

  • :echo Output debug information.

  • :silent Do not output any warnings.

  • :colorize Colorize output

  • :output All report outputs get << through this output.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/request_log_analyzer/controller.rb', line 113

def initialize(source, options = {})

  @source      = source
  @options     = options
  @aggregators = []
  @filters     = []
  @output      = options[:output]
  
  # Register the request format for this session after checking its validity
  raise "Invalid file format!" unless @source.file_format.valid?
  
  # Install event handlers for wrnings, progress updates and source changes
  @source.warning        = lambda { |type, message, lineno|  @aggregators.each { |agg| agg.warning(type, message, lineno) } }
  @source.progress       = lambda { |message, value| handle_progress(message, value) } unless options[:no_progress]
  @source.source_changes = lambda { |change, filename| handle_source_change(change, filename) }
end

Instance Attribute Details

#aggregatorsObject (readonly)

Returns the value of attribute aggregators.



20
21
22
# File 'lib/request_log_analyzer/controller.rb', line 20

def aggregators
  @aggregators
end

#filtersObject (readonly)

Returns the value of attribute filters.



20
21
22
# File 'lib/request_log_analyzer/controller.rb', line 20

def filters
  @filters
end

#optionsObject (readonly)

Returns the value of attribute options.



20
21
22
# File 'lib/request_log_analyzer/controller.rb', line 20

def options
  @options
end

#outputObject (readonly)

Returns the value of attribute output.



20
21
22
# File 'lib/request_log_analyzer/controller.rb', line 20

def output
  @output
end

#sourceObject (readonly)

Returns the value of attribute source.



20
21
22
# File 'lib/request_log_analyzer/controller.rb', line 20

def source
  @source
end

Class Method Details

.build(arguments) ⇒ Object

Builds a RequestLogAnalyzer::Controller given parsed command line arguments arguments<tt> A CommandLine::Arguments hash containing parsed commandline parameters. <rr>report_with Width of the report. Defaults to 80.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/request_log_analyzer/controller.rb', line 25

def self.build(arguments)
  options = { }

  # Database command line options
  options[:database]       = arguments[:database] if arguments[:database]
  options[:reset_database] = arguments[:reset_database]
  options[:debug]          = arguments[:debug]
  options[:dump]           = arguments[:dump]
  options[:parse_strategy] = arguments[:parse_strategy]
  options[:no_progress]    = arguments[:no_progress]
  
  output_class = RequestLogAnalyzer::Output::const_get(arguments[:output])
  if arguments[:file]
    output_file = File.new(arguments[:file], "w+")
    options[:output] = output_class.new(output_file, :width => 80, :color => false, :characters => :ascii)
  elsif arguments[:mail]
    output_mail = RequestLogAnalyzer::Mailer.new(arguments[:mail])
    options[:output] = output_class.new(output_mail, :width => 80, :color => false, :characters => :ascii)        
  else
    options[:output] = output_class.new(STDOUT, :width => arguments[:report_width].to_i, 
        :color => !arguments[:boring], :characters => (arguments[:boring] ? :ascii : :utf))
  end
            
  # Create the controller with the correct file format
  file_format = if arguments[:apache_format]
      RequestLogAnalyzer::FileFormat.load(:apache, arguments[:apache_format])
    else 
      RequestLogAnalyzer::FileFormat.load(arguments[:format])
    end

  # register sources
  if arguments.parameters.length == 1
    file = arguments.parameters[0]
    if file == '-' || file == 'STDIN'
      options.store(:source_files, $stdin)
    elsif File.exist?(file)
      options.store(:source_files, file)
    else
      puts "File not found: #{file}"
      exit(0)
    end
  else
    options.store(:source_files, arguments.parameters)
  end
  
  controller = Controller.new(RequestLogAnalyzer::Source::LogParser.new(file_format, options), options)
  #controller = Controller.new(RequestLogAnalyzer::Source::DatabaseLoader.new(file_format, options), options)
  
  # register filters
  if arguments[:after] || arguments[:before]
    filter_options = {}
    filter_options[:after]  = DateTime.parse(arguments[:after])  
    filter_options[:before] = DateTime.parse(arguments[:before]) if arguments[:before]
    controller.add_filter(:timespan, filter_options)
  end
  
  arguments[:reject].each do |(field, value)|
    controller.add_filter(:field, :mode => :reject, :field => field, :value => value)
  end
  
  arguments[:select].each do |(field, value)|
    controller.add_filter(:field, :mode => :select, :field => field, :value => value)
  end

  # register aggregators
  arguments[:aggregator].each { |agg| controller.add_aggregator(agg.to_sym) }

  # register the database 
  controller.add_aggregator(:summarizer)          if arguments[:aggregator].empty?
  controller.add_aggregator(:database_inserter)   if arguments[:database] && !arguments[:aggregator].include?('database')

  # register the echo aggregator in debug mode
  controller.add_aggregator(:echo) if arguments[:debug]
  
  file_format.setup_environment(controller)
      
  return controller
end

Instance Method Details

#add_aggregator(agg) ⇒ Object Also known as: >>

Adds an aggregator to the controller. The aggregator will be called for every request that is parsed from the provided sources (see add_source)



158
159
160
161
# File 'lib/request_log_analyzer/controller.rb', line 158

def add_aggregator(agg)      
  agg = RequestLogAnalyzer::Aggregator.const_get(RequestLogAnalyzer::to_camelcase(agg)) if agg.kind_of?(Symbol)
  @aggregators << agg.new(@source, @options)
end

#add_filter(filter, filter_options = {}) ⇒ Object

Adds a request filter to the controller.



166
167
168
169
# File 'lib/request_log_analyzer/controller.rb', line 166

def add_filter(filter, filter_options = {})
  filter = RequestLogAnalyzer::Filter.const_get(RequestLogAnalyzer::to_camelcase(filter)) if filter.kind_of?(Symbol)
  @filters << filter.new(source.file_format, @options.merge(filter_options))
end

#aggregate_request(request) ⇒ Object

Push a request to all the aggregators (@aggregators). request The request to push to the aggregators.



184
185
186
187
188
# File 'lib/request_log_analyzer/controller.rb', line 184

def aggregate_request(request)
  return false unless request
  @aggregators.each { |agg| agg.aggregate(request) }
  return true
end

#filter_request(request) ⇒ Object

Push a request through the entire filterchain (@filters). request The request to filter. Returns the filtered request or nil.



174
175
176
177
178
179
180
# File 'lib/request_log_analyzer/controller.rb', line 174

def filter_request(request)
  @filters.each do |filter| 
    request = filter.filter(request)
    return nil if request.nil?
  end
  return request
end

#handle_progress(message, value = nil) ⇒ Object

Progress function. Expects :started with file, :progress with current line and :finished or :interrupted when done. message Current state (:started, :finished, :interupted or :progress). value File or current line.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/request_log_analyzer/controller.rb', line 134

def handle_progress(message, value = nil)
  case message
  when :started
    @progress_bar = CommandLine::ProgressBar.new(File.basename(value), File.size(value), STDOUT)
  when :finished
    @progress_bar.finish
    @progress_bar = nil
  when :interrupted
    if @progress_bar
      @progress_bar.halt
      @progress_bar = nil
    end
  when :progress
    @progress_bar.set(value)
  end
end

#handle_source_change(change, filename) ⇒ Object

Source change handler



152
153
154
# File 'lib/request_log_analyzer/controller.rb', line 152

def handle_source_change(change, filename)
  @aggregators.each { |agg| agg.source_change(change, File.expand_path(filename, Dir.pwd)) }
end

#install_signal_handlersObject



228
229
230
231
232
233
234
# File 'lib/request_log_analyzer/controller.rb', line 228

def install_signal_handlers
  Signal.trap("INT") do
    handle_progress(:interrupted)
    puts "Caught interrupt! Stopping parsing..."
    @interrupted = true
  end
end

#run!Object

Runs RequestLogAnalyzer

  1. Call prepare on every aggregator

  2. Generate requests from source object

  3. Filter out unwanted requests

  4. Call aggregate for remaning requests on every aggregator

  5. Call finalize on every aggregator

  6. Call report on every aggregator

  7. Finalize Source



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/request_log_analyzer/controller.rb', line 198

def run!
  
  @aggregators.each { |agg| agg.prepare }
  install_signal_handlers
  
  @source.each_request do |request|
    break if @interrupted
    aggregate_request(filter_request(request))
  end

  @aggregators.each { |agg| agg.finalize }

  @output.header
  @aggregators.each { |agg| agg.report(@output) }
  @output.footer
        
  @source.finalize
  
  if @output.io.kind_of?(File)
    puts
    puts "Report written to: " + File.expand_path(@output.io.path)
    puts "Need an expert to analyze your application?"
    puts "Mail to [email protected] or visit us at http://railsdoctors.com"
    puts "Thanks for using request-log-analyzer!"
    @output.io.close
  elsif @output.io.kind_of?(RequestLogAnalyzer::Mailer)
    @output.io.mail
  end
end