lackac-request-log-analyzer 0.1.3

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.
@@ -0,0 +1,139 @@
1
+ require 'rubygems'
2
+ require 'sqlite3'
3
+
4
+ module Base
5
+
6
+ # Set of functions that can be used to easily log requests into a SQLite3 Database.
7
+ class RecordInserter
8
+
9
+ attr_reader :database
10
+ attr_reader :current_request
11
+ attr_reader :warning_count
12
+
13
+ # Initializer
14
+ # <tt>db_file</tt> The file which will be used for the SQLite3 Database storage.
15
+ def initialize(db_file, options = {})
16
+ @database = SQLite3::Database.new(db_file)
17
+ @insert_statements = nil
18
+ @warning_count = 0
19
+ create_tables_if_needed!
20
+
21
+ self.initialize_hook(options) if self.respond_to?(:initialize_hook)
22
+ end
23
+
24
+ # Calculate the database durations of the requests currenty in the database.
25
+ # Used if a logfile does contain any database durations.
26
+ def calculate_db_durations!
27
+ @database.execute('UPDATE "completed_queries" SET "database" = "duration" - "rendering" WHERE "database" IS NULL OR "database" = 0.0')
28
+ end
29
+
30
+ # Insert a batch of loglines into the database.
31
+ # Function prepares insert statements, yeilds and then closes and commits.
32
+ def insert_batch(&block)
33
+ @database.transaction
34
+ prepare_statements!
35
+ block.call(self)
36
+ close_prepared_statements!
37
+ @database.commit
38
+ rescue Exception => e
39
+ puts e.message
40
+ @database.rollback
41
+ end
42
+
43
+ def insert_warning(line, warning)
44
+ @database.execute("INSERT INTO parse_warnings (line, warning) VALUES (:line, :warning)", :line => line, :warning => warning)
45
+ @warning_count += 1
46
+ end
47
+
48
+ # Insert a request into the database.
49
+ # def insert(request, close_statements = false)
50
+ # raise 'No insert defined for this log file type'
51
+ # end
52
+
53
+ # Insert a batch of files into the database.
54
+ # <tt>db_file</tt> The filename of the database file to use.
55
+ # Returns the created database.
56
+ def self.insert_batch_into(db_file, options = {}, &block)
57
+ db = self.new(db_file)
58
+ db.insert_batch(&block)
59
+ return db
60
+ end
61
+
62
+ def count(type)
63
+ @database.get_first_value("SELECT COUNT(*) FROM \"#{type}_requests\"").to_i
64
+ end
65
+
66
+ protected
67
+
68
+ # Prepare insert statements.
69
+ def prepare_statements!
70
+ @insert_statements = {
71
+ :started => @database.prepare("
72
+ INSERT INTO started_requests ( line, timestamp, ip, method, controller, action)
73
+ VALUES (:line, :timestamp, :ip, :method, :controller, :action)"),
74
+
75
+ :failed => @database.prepare("
76
+ INSERT INTO failed_requests ( line, exception_string, stack_trace, error)
77
+ VALUES (:line, :exception_string, :stack_trace, :error)"),
78
+
79
+ :completed => @database.prepare("
80
+ INSERT INTO completed_requests ( line, url, status, duration, rendering_time, database_time)
81
+ VALUES (:line, :url, :status, :duration, :rendering, :db)")
82
+ }
83
+ end
84
+
85
+ # Close all prepared statments
86
+ def close_prepared_statements!
87
+ @insert_statements.each { |key, stmt| stmt.close }
88
+ end
89
+
90
+ # Create the needed database tables if they don't exist.
91
+ def create_tables_if_needed!
92
+
93
+ @database.execute("
94
+ CREATE TABLE IF NOT EXISTS started_requests (
95
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
96
+ line INTEGER NOT NULL,
97
+ timestamp DATETIME NOT NULL,
98
+ controller VARCHAR(255) NOT NULL,
99
+ action VARCHAR(255) NOT NULL,
100
+ method VARCHAR(6) NOT NULL,
101
+ ip VARCHAR(6) NOT NULL
102
+ )
103
+ ");
104
+
105
+ @database.execute("
106
+ CREATE TABLE IF NOT EXISTS failed_requests (
107
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
108
+ line INTEGER NOT NULL,
109
+ started_request_id INTEGER,
110
+ error VARCHAR(255),
111
+ exception_string VARCHAR(255),
112
+ stack_trace TEXT
113
+ )
114
+ ");
115
+
116
+ @database.execute("
117
+ CREATE TABLE IF NOT EXISTS completed_requests (
118
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
119
+ line INTEGER NOT NULL,
120
+ started_request_id INTEGER,
121
+ url VARCHAR(255) NOT NULL,
122
+ hashed_url VARCHAR(255),
123
+ status INTEGER NOT NULL,
124
+ duration FLOAT,
125
+ rendering_time FLOAT,
126
+ database_time FLOAT
127
+ )
128
+ ");
129
+
130
+ @database.execute("CREATE TABLE IF NOT EXISTS parse_warnings (
131
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
132
+ line INTEGER NOT NULL,
133
+ warning VARCHAR(255) NOT NULL
134
+ )
135
+ ");
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,71 @@
1
+ module Base
2
+
3
+ # Functions to summarize an array of requets.
4
+ # Can calculate request counts, duratations, mean times etc. of all the requests given.
5
+ class Summarizer
6
+ attr_reader :actions
7
+ attr_reader :errors
8
+ attr_reader :request_count
9
+ attr_reader :request_time_graph
10
+ attr_reader :first_request_at
11
+ attr_reader :last_request_at
12
+ attr_reader :methods
13
+
14
+ attr_accessor :blocker_duration
15
+ DEFAULT_BLOCKER_DURATION = 1.0
16
+
17
+ # Initializer. Sets global variables
18
+ # Options
19
+ def initialize(options = {})
20
+ @actions = {}
21
+ @blockers = {}
22
+ @errors = {}
23
+ @request_count = 0
24
+ @blocker_duration = DEFAULT_BLOCKER_DURATION
25
+ @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]
26
+ @methods = {:GET => 0, :POST => 0, :PUT => 0, :DELETE => 0}
27
+
28
+ self.initialize_hook(options) if self.respond_to?(:initialize_hook)
29
+ end
30
+
31
+ # Check if any of the request parsed had a timestamp.
32
+ def has_timestamps?
33
+ @first_request_at
34
+ end
35
+
36
+ # Calculate the duration of a request
37
+ # Returns a DateTime object if possible, 0 otherwise.
38
+ def duration
39
+ (@last_request_at && @first_request_at) ? (DateTime.parse(@last_request_at) - DateTime.parse(@first_request_at)).ceil : 0
40
+ end
41
+
42
+ # Check if the request time graph usable data.
43
+ def request_time_graph?
44
+ @request_time_graph.uniq != [0] && duration > 0
45
+ end
46
+
47
+ # Return a list of requests sorted on a specific action field
48
+ # <tt>field</tt> The action field to sort by.
49
+ # <tt>min_count</tt> Values which fall below this amount are not returned (default nil).
50
+ def sort_actions_by(field, min_count = nil)
51
+ actions = min_count.nil? ? @actions.to_a : @actions.delete_if { |k, v| v[:count] < min_count}.to_a
52
+ actions.sort { |a, b| (a[1][field.to_sym] <=> b[1][field.to_sym]) }
53
+ end
54
+
55
+ # Returns a list of request blockers sorted by a specific field
56
+ # <tt>field</tt> The action field to sort by.
57
+ # <tt>min_count</tt> Values which fall below this amount are not returned (default @blocker_duration).
58
+ def sort_blockers_by(field, min_count = @blocker_duration)
59
+ blockers = min_count.nil? ? @blockers.to_a : @blockers.delete_if { |k, v| v[:count] < min_count}.to_a
60
+ blockers.sort { |a, b| a[1][field.to_sym] <=> b[1][field.to_sym] }
61
+ end
62
+
63
+ # Returns a list of request blockers sorted by a specific field
64
+ # <tt>field</tt> The action field to sort by.
65
+ # <tt>min_count</tt> Values which fall below this amount are not returned (default @blocker_duration).
66
+ def sort_errors_by(field, min_count = nil)
67
+ errors = min_count.nil? ? @errors.to_a : @errors.delete_if { |k, v| v[:count] < min_count}.to_a
68
+ errors.sort { |a, b| a[1][field.to_sym] <=> b[1][field.to_sym] }
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,60 @@
1
+ # Colorize a text output with the given color if.
2
+ # <tt>text</tt> The text to colorize.
3
+ # <tt>color_code</tt> The color code string to set
4
+ # <tt>color</tt> Does not color if false. Defaults to ($arguments && $arguments[:colorize])
5
+ def colorize(text, color_code, color = $arguments && $arguments[:colorize])
6
+ color ? "#{color_code}#{text}\e[0m" : text
7
+ end
8
+
9
+ # Draw a red line of text
10
+ def red(text)
11
+ colorize(text, "\e[31m")
12
+ end
13
+
14
+ # Draw a Green line of text
15
+ def green(text)
16
+ colorize(text, "\e[32m")
17
+ end
18
+
19
+ # Draw a Yellow line of text
20
+ def yellow(text)
21
+ colorize(text, "\e[33m")
22
+ end
23
+
24
+ # Draw a Yellow line of text
25
+ def blue(text)
26
+ colorize(text, "\e[34m")
27
+ end
28
+
29
+ def white(text)
30
+ colorize(text, "\e[37m")
31
+ end
32
+
33
+
34
+ #STYLE = {
35
+ # :default => “33[0m”,
36
+ # # styles
37
+ # :bold => “33[1m”,
38
+ # :underline => “33[4m”,
39
+ # :blink => “33[5m”,
40
+ # :reverse => “33[7m”,
41
+ # :concealed => “33[8m”,
42
+ # # font colors
43
+ # :black => “33[30m”,
44
+ # :red => “33[31m”,
45
+ # :green => “33[32m”,
46
+ # :yellow => “33[33m”,
47
+ # :blue => “33[34m”,
48
+ # :magenta => “33[35m”,
49
+ # :cyan => “33[36m”,
50
+ # :white => “33[37m”,
51
+ # # background colors
52
+ # :on_black => “33[40m”,
53
+ # :on_red => “33[41m”,
54
+ # :on_green => “33[42m”,
55
+ # :on_yellow => “33[43m”,
56
+ # :on_blue => “33[44m”,
57
+ # :on_magenta => “33[45m”,
58
+ # :on_cyan => “33[46m”,
59
+ # :on_white => “33[47m” }
60
+ #
@@ -0,0 +1,129 @@
1
+ require "#{File.dirname(__FILE__)}/flag"
2
+ require "#{File.dirname(__FILE__)}/exceptions"
3
+
4
+ # Module used to parse commandline arguments
5
+ module CommandLine
6
+
7
+ # Parse argument lists and return an argument object containing all set flags and switches.
8
+ class Arguments
9
+
10
+ FLAG_REGEXP = /^--?[A-z0-9]/
11
+
12
+ attr_reader :flag_definitions
13
+
14
+ attr_reader :flags
15
+ attr_reader :files
16
+ attr_reader :command
17
+
18
+ attr_accessor :required_files
19
+
20
+ # Initializer.
21
+ # <tt>arguments</tt> The arguments which are going to be parsed (defaults to $*).
22
+ def initialize(arguments = $*, &block)
23
+ @arguments = arguments
24
+ @flag_definitions = {}
25
+ @begins_with_command = false
26
+ end
27
+
28
+ # Parse a list of arguments. Intatiates a Argument object with the given arguments and yeilds
29
+ # it so that flags and switches can be set by the application.
30
+ # <tt>arguments</tt> The arguments which are going to be parsed (defaults to $*).
31
+ # Returns the arguments object.parse!
32
+ def self.parse(arguments = $*, &block)
33
+ cla = Arguments.new(arguments)
34
+ yield(cla)
35
+ return cla.parse!
36
+ end
37
+
38
+ # Handle argument switches for the application
39
+ # <tt>switch</tt> A switch symbol like :fast
40
+ # <tt>switch_alias</tt> An short alias for the same switch (:f).
41
+ def switch(switch, switch_alias = nil)
42
+ return self.flag(switch, :alias => switch_alias, :expects => nil)
43
+ end
44
+
45
+ # Handle argument flags for the application
46
+ # <tt>flag</tt> A flag symbol like :fast
47
+ # Options
48
+ # * <tt>:expects</tt> Expects a value after the flag
49
+ def flag(flag, options)
50
+ options[:expects] = String unless options.has_key?(:expects)
51
+ argument = Flag.new(flag, options)
52
+ @flag_definitions[argument.to_argument] = argument
53
+ @flag_definitions[argument.to_alias] = argument if argument.has_alias?
54
+ return argument
55
+ end
56
+
57
+ # If called argument list must begin with a command.
58
+ # <tt>begins_w_command</tt> Defaults to true.
59
+ def begins_with_command!(begins_w_command=true)
60
+ @begins_with_command = begins_w_command
61
+ end
62
+
63
+ # Unknown flags will be silently ignored.
64
+ # <tt>ignore</tt> Defaults to true.
65
+ def ignore_unknown_flags!(ignore = true)
66
+ @ignore_unknown_flags = ignore
67
+ end
68
+
69
+ def [](name)
70
+ return flags[name.to_s.gsub(/_/, '-').to_sym]
71
+ end
72
+
73
+ # Parse the flags and switches set by the application.
74
+ # Returns an arguments object containing the flags and switches found in the commandline.
75
+ def parse!
76
+ @flags = {}
77
+ @files = []
78
+
79
+ i = 0
80
+ while @arguments.length > i do
81
+ arg = @arguments[i]
82
+ if FLAG_REGEXP =~ arg
83
+ if @flag_definitions.has_key?(arg)
84
+ flag = @flag_definitions[arg]
85
+ if flag.expects_argument?
86
+
87
+ if @arguments.length > (i + 1) && @arguments[i + 1]
88
+ @flags[flag.name] = @arguments[i + 1]
89
+ i += 1
90
+ else
91
+ raise CommandLine::FlagExpectsArgument.new(arg)
92
+ end
93
+
94
+ else
95
+ @flags[flag.name] = true
96
+ end
97
+ else
98
+ raise CommandLine::UnknownFlag.new(arg) unless @ignore_unknown_flags
99
+ end
100
+ else
101
+ if @begins_with_command && @command.nil?
102
+ @command = arg
103
+ else
104
+ @files << arg
105
+ end
106
+ end
107
+ i += 1
108
+ end
109
+
110
+ check_parsed_arguments!
111
+
112
+ return self
113
+ end
114
+
115
+ # Check if the parsed arguments meet their requirements.
116
+ # Raises CommandLineexception on error.
117
+ def check_parsed_arguments!
118
+ if @begins_with_command && @command.nil?
119
+ raise CommandLine::CommandMissing.new
120
+ end
121
+
122
+ if @required_files && @files.length < @required_files
123
+ raise CommandLine::FileMissing.new("You need at least #{@required_files} files")
124
+ end
125
+
126
+ end
127
+ end
128
+
129
+ end
@@ -0,0 +1,37 @@
1
+ module CommandLine
2
+
3
+ # Commandline parsing errors and exceptions
4
+ class Error < Exception
5
+ end
6
+
7
+ # Missing a required flag
8
+ class FlagMissing < CommandLine::Error
9
+ end
10
+
11
+ # Missing a required file
12
+ class FileMissing < CommandLine::Error
13
+ end
14
+
15
+ # Missing a required flag argument
16
+ class FlagExpectsArgument < CommandLine::Error
17
+ def initialize(flag)
18
+ super("#{flag} expects an argument!")
19
+ end
20
+ end
21
+
22
+ # Missing a required command
23
+ class CommandMissing < CommandLine::Error
24
+ def initialize(msg = "A command is missing")
25
+ super(msg)
26
+ end
27
+
28
+ end
29
+
30
+ # Encountered an unkown flag
31
+ class UnknownFlag < CommandLine::Error
32
+ def initialize(flag)
33
+ super("#{flag} not recognized as a valid flag!")
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,51 @@
1
+ module CommandLine
2
+
3
+ # Argument flag handling.
4
+ class Flag
5
+
6
+ attr_reader :name
7
+ attr_reader :alias
8
+ attr_reader :argument
9
+
10
+ # Initialize new Flag
11
+ # <tt>name</tt> The name of the flag
12
+ # <tt>definition</tt> The definition of the flag.
13
+ def initialize(name, definition)
14
+ @name = name.to_s.gsub(/_/, '-').to_sym
15
+ @alias = definition[:alias].to_sym if definition[:alias]
16
+ @required = definition.has_key?(:required) && definition[:required] == true
17
+ @argument = definition[:expects] if definition[:expects]
18
+ end
19
+
20
+ # Argument representation of the flag (--fast)
21
+ def to_argument
22
+ "--#{@name}"
23
+ end
24
+
25
+ # Argument alias representation of the flag (-f)
26
+ def to_alias
27
+ "-#{@alias}"
28
+ end
29
+
30
+ # Check if flag has an alias
31
+ def has_alias?
32
+ !@alias.nil?
33
+ end
34
+
35
+ # Check if flag is optional
36
+ def optional?
37
+ !@required
38
+ end
39
+
40
+ # Check if flag is required
41
+ def required?
42
+ @required
43
+ end
44
+
45
+ # Check if flag expects an argument (Are you talking to me?)
46
+ def expects_argument?
47
+ !@argument.nil?
48
+ end
49
+ end
50
+
51
+ end