log_sense 1.0.6 → 1.0.7
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 +4 -4
- data/Gemfile.lock +2 -2
- data/LICENSE.txt +1 -1
- data/exe/log_sense +3 -2
- data/lib/log_sense/apache_data_cruncher.rb +14 -4
- data/lib/log_sense/emitter.rb +1 -7
- data/lib/log_sense/options_parser.rb +20 -8
- data/lib/log_sense/rails_data_cruncher.rb +12 -3
- data/lib/log_sense/rails_log_parser.rb +4 -1
- data/lib/log_sense/templates/_command_invocation.html.erb +29 -0
- data/lib/log_sense/templates/_command_invocation.txt.erb +6 -0
- data/lib/log_sense/templates/_output_table.html.erb +1 -1
- data/lib/log_sense/templates/_performance.html.erb +23 -0
- data/lib/log_sense/templates/_performance.txt.erb +9 -0
- data/lib/log_sense/templates/_summary.html.erb +34 -0
- data/lib/log_sense/templates/_summary.txt.erb +10 -0
- data/lib/log_sense/templates/_total_hits.html.erb +32 -0
- data/lib/log_sense/templates/apache.html.erb +216 -299
- data/lib/log_sense/templates/rails.txt.erb +22 -7
- data/lib/log_sense/version.rb +1 -1
- data/sample_logs/empty_log.log +0 -0
- data/sample_logs/safety-critical_org.log +364 -0
- data/sample_logs/spmbook_com.log +1636 -0
- metadata +13 -7
- data/alr-styles.css +0 -61
- data/lib/log_sense/templates/#apache.org.erb# +0 -266
- data/lib/log_sense/templates/.#apache.org.erb +0 -1
- data/lib/log_sense/templates/apache.org.erb +0 -266
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f2fcc8402c27ec959854e3cd032e9e75693e538d3ca0879de3f4cf0b6aae9c5
|
4
|
+
data.tar.gz: e2530ff646c436ca88e674a698bf71876f2e62bc0b59f20ebbc4e38fdd787830
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30932a09a0a0d0717c4e5caa9a579bb60a3a930dc144dc318029945e0364e71e5ddf5d5868ccf758a11d3ed5f73f5d690f482521e3475f1f16a5a041fa4abd8a
|
7
|
+
data.tar.gz: da8ac76f281cf9f8c67805621fe098d1480889f2fb5a55a378d1ffae3383c1913d572c28c9a68c876fb710b4e8bc3213b157c51cdf008f1505c320ef5fabedd0
|
data/Gemfile.lock
CHANGED
data/LICENSE.txt
CHANGED
data/exe/log_sense
CHANGED
@@ -6,10 +6,11 @@ require 'log_sense.rb'
|
|
6
6
|
# Parse Command Line Arguments
|
7
7
|
#
|
8
8
|
|
9
|
-
# better be here... OptionsParser consumes ARGV
|
9
|
+
# this better be here... OptionsParser consumes ARGV
|
10
10
|
@command_line = ARGV.join(" ")
|
11
|
+
|
11
12
|
@options = LogSense::OptionsParser.parse ARGV
|
12
|
-
@input_file = @options[:input_file]
|
13
|
+
@input_file = @options[:input_file] || ARGV[0]
|
13
14
|
@output_file = @options[:output_file]
|
14
15
|
|
15
16
|
if not @input_file
|
@@ -11,8 +11,8 @@ module LogSense
|
|
11
11
|
last_day_s = db.execute "SELECT datetime from LogLine order by datetime desc limit 1"
|
12
12
|
|
13
13
|
# make first and last day into dates or nil
|
14
|
-
@first_day = first_day_s
|
15
|
-
@last_day = last_day_s
|
14
|
+
@first_day = first_day_s[0][0] ? nil : Date.parse(first_day_s[0][0])
|
15
|
+
@last_day = last_day_s[0][0] ? nil : Date.parse(last_day_s[0][0])
|
16
16
|
|
17
17
|
@total_days = 0
|
18
18
|
if @first_day and @last_day
|
@@ -20,8 +20,13 @@ module LogSense
|
|
20
20
|
end
|
21
21
|
|
22
22
|
@log_size = db.execute "SELECT count(datetime) from LogLine"
|
23
|
-
@
|
23
|
+
@log_size = @log_size[0][0]
|
24
|
+
|
24
25
|
@selfpolls_size = db.execute "SELECT count(datetime) from LogLine where ip == '::1'"
|
26
|
+
@selfpolls_size = @selfpolls_size[0][0]
|
27
|
+
|
28
|
+
@crawlers_size = db.execute "SELECT count(datetime) from LogLine where bot == 1"
|
29
|
+
@crawlers_size = @crawlers_size[0][0]
|
25
30
|
|
26
31
|
@first_day_requested = options[:from_date]
|
27
32
|
@last_day_requested = options[:to_date]
|
@@ -74,8 +79,13 @@ module LogSense
|
|
74
79
|
EOS
|
75
80
|
|
76
81
|
@total_hits = db.execute "SELECT count(datetime) from LogLine where #{filter}"
|
77
|
-
@
|
82
|
+
@total_hits = @total_hits[0][0]
|
83
|
+
|
84
|
+
@total_unique_visits = db.execute "SELECT count(distinct(unique_visitor)) from LogLine where #{filter}"
|
85
|
+
@total_unique_visits = @total_unique_visits[0][0]
|
86
|
+
|
78
87
|
@total_size = db.execute "SELECT #{human_readable_size} from LogLine where #{filter}"
|
88
|
+
@total_size = @total_size[0][0]
|
79
89
|
|
80
90
|
@daily_distribution = db.execute "SELECT date(datetime), #{human_readable_day}, count(datetime), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where #{filter} group by date(datetime)"
|
81
91
|
@time_distribution = db.execute "SELECT strftime('%H', datetime), count(datetime), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where #{filter} group by strftime('%H', datetime)"
|
data/lib/log_sense/emitter.rb
CHANGED
@@ -33,14 +33,8 @@ module LogSense
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
-
def self.output_txt_table name, headings, rows
|
37
|
-
name = "#+NAME: #{name}"
|
38
|
-
table = Terminal::Table.new headings: headings, rows: rows, style: { border_x: "-", border_i: "|" }
|
39
|
-
name + "\n" + table.to_s
|
40
|
-
end
|
41
|
-
|
42
36
|
def self.render(template, vars)
|
43
|
-
@template = File.join(File.dirname(__FILE__), "templates", "_#{template}
|
37
|
+
@template = File.join(File.dirname(__FILE__), "templates", "_#{template}")
|
44
38
|
erb_template = File.read @template
|
45
39
|
ERB.new(erb_template).result(OpenStruct.new(vars).instance_eval { binding })
|
46
40
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'optparse/date'
|
3
|
-
require '
|
3
|
+
require 'log_sense/version'
|
4
4
|
|
5
5
|
module LogSense
|
6
6
|
module OptionsParser
|
@@ -14,19 +14,19 @@ module LogSense
|
|
14
14
|
opt_parser = OptionParser.new do |opts|
|
15
15
|
opts.banner = "Usage: log_sense [options] [logfile]"
|
16
16
|
|
17
|
-
opts.on("-fFORMAT", "--
|
17
|
+
opts.on("-fFORMAT", "--input-format=FORMAT", String, "Input format (either rails or apache)") do |n|
|
18
18
|
args[:input_format] = n
|
19
19
|
end
|
20
20
|
|
21
|
-
opts.on("-iINPUT_FILE", "--input=INPUT_FILE", String, "Input file") do |n|
|
21
|
+
opts.on("-iINPUT_FILE", "--input-file=INPUT_FILE", String, "Input file") do |n|
|
22
22
|
args[:input_file] = n
|
23
23
|
end
|
24
24
|
|
25
|
-
opts.on("-tFORMAT", "--
|
25
|
+
opts.on("-tFORMAT", "--output-format=FORMAT", String, "Output format: html, org, txt, sqlite. Defaults to org mode") do |n|
|
26
26
|
args[:output_format] = n
|
27
27
|
end
|
28
28
|
|
29
|
-
opts.on("-oOUTPUT_FILE", "--output=OUTPUT_FILE", String, "Output file") do |n|
|
29
|
+
opts.on("-oOUTPUT_FILE", "--output-file=OUTPUT_FILE", String, "Output file") do |n|
|
30
30
|
args[:output_file] = n
|
31
31
|
end
|
32
32
|
|
@@ -57,16 +57,28 @@ module LogSense
|
|
57
57
|
|
58
58
|
opts.on("-v", "--version", "Prints version information") do
|
59
59
|
puts "log_sense version #{LogSense::VERSION}"
|
60
|
-
puts "Copyright (C)
|
60
|
+
puts "Copyright (C) 2021 Shair.Tech"
|
61
61
|
puts "Distributed under the terms of the MIT license"
|
62
|
-
puts ""
|
63
|
-
puts "Written by Adolfo Villafiorita"
|
64
62
|
exit
|
65
63
|
end
|
66
64
|
|
67
65
|
opts.on("-h", "--help", "Prints this help") do
|
68
66
|
puts opts
|
67
|
+
puts ""
|
69
68
|
puts "This is version #{LogSense::VERSION}"
|
69
|
+
|
70
|
+
puts ""
|
71
|
+
puts "Output formats"
|
72
|
+
pathname = File.join(File.dirname(__FILE__), "templates", "*")
|
73
|
+
templates = Dir.glob(pathname).select { |x| ! File.basename(x).start_with? /_|#/ and ! File.basename(x).end_with? "~" }
|
74
|
+
components = templates.map { |x| File.basename(x).split "." }.group_by { |x| x[0] }
|
75
|
+
components.each do |k, vs|
|
76
|
+
puts "#{k} parsing can produce the following outputs:"
|
77
|
+
vs.each do |v|
|
78
|
+
puts " - #{v[1]}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
70
82
|
exit
|
71
83
|
end
|
72
84
|
end
|
@@ -8,22 +8,28 @@ module LogSense
|
|
8
8
|
#
|
9
9
|
|
10
10
|
def self.crunch db, options = { limit: 30 }
|
11
|
-
first_day_s = db.execute "SELECT started_at from Event order by started_at limit 1"
|
11
|
+
first_day_s = db.execute "SELECT started_at from Event where started_at not NULL order by started_at limit 1"
|
12
12
|
# we could use ended_at to cover the full activity period, but I prefer started_at
|
13
13
|
# with the meaning that the monitor event initiation
|
14
14
|
last_day_s = db.execute "SELECT started_at from Event order by started_at desc limit 1"
|
15
15
|
|
16
16
|
# make first and last day into dates or nil
|
17
17
|
# TODO: bug possible value here: [[nil]], which is not empty
|
18
|
-
@first_day = first_day_s
|
19
|
-
@last_day = last_day_s
|
18
|
+
@first_day = ! first_day_s[0][0] ? nil : Date.parse(first_day_s[0][0])
|
19
|
+
@last_day = ! last_day_s[0][0] ? nil : Date.parse(last_day_s[0][0])
|
20
20
|
|
21
21
|
@total_days = 0
|
22
22
|
if @first_day and @last_day
|
23
23
|
@total_days = (@last_day - @first_day).to_i
|
24
24
|
end
|
25
25
|
|
26
|
+
@log_size = db.execute "SELECT count(started_at) from Event"
|
27
|
+
@log_size = @log_size[0][0]
|
28
|
+
|
29
|
+
# SAME AS ABOVE (but log_size is wrong in the case of Rails
|
30
|
+
# logs, since an event takes more than one line)
|
26
31
|
@events = db.execute "SELECT count(started_at) from Event"
|
32
|
+
@events = @events[0][0]
|
27
33
|
|
28
34
|
@first_day_requested = options[:from_date]
|
29
35
|
@last_day_requested = options[:to_date]
|
@@ -74,6 +80,9 @@ module LogSense
|
|
74
80
|
|
75
81
|
@total_events = db.execute "SELECT count(started_at) from Event where #{filter}"
|
76
82
|
|
83
|
+
@total_unique_visits = db.execute "SELECT count(distinct(unique_visitor)) from Event where #{filter}"
|
84
|
+
@total_unique_visits = @total_unique_visits[0][0]
|
85
|
+
|
77
86
|
@daily_distribution = db.execute "SELECT date(started_at), #{human_readable_day}, count(started_at) from Event where #{filter} group by date(started_at)"
|
78
87
|
@time_distribution = db.execute "SELECT strftime('%H', started_at), count(started_at) from Event where #{filter} group by strftime('%H', started_at)"
|
79
88
|
|
@@ -12,6 +12,7 @@ module LogSense
|
|
12
12
|
ended_at TEXT,
|
13
13
|
log_id TEXT,
|
14
14
|
ip TEXT,
|
15
|
+
unique_visitor TEXT,
|
15
16
|
url TEXT,
|
16
17
|
controller TEXT,
|
17
18
|
html_verb TEXT,
|
@@ -27,6 +28,7 @@ module LogSense
|
|
27
28
|
ended_at,
|
28
29
|
log_id,
|
29
30
|
ip,
|
31
|
+
unique_visitor,
|
30
32
|
url,
|
31
33
|
controller,
|
32
34
|
html_verb,
|
@@ -35,7 +37,7 @@ module LogSense
|
|
35
37
|
duration_views_ms,
|
36
38
|
duration_ar_ms,
|
37
39
|
allocations)
|
38
|
-
values (#{Array.new(
|
40
|
+
values (#{Array.new(13, '?').join(', ')})")
|
39
41
|
|
40
42
|
# requests in the log might be interleaved.
|
41
43
|
#
|
@@ -90,6 +92,7 @@ module LogSense
|
|
90
92
|
event[:ended_at],
|
91
93
|
event[:log_id],
|
92
94
|
event[:ip],
|
95
|
+
"#{DateTime.parse(event[:ended_at]).strftime("%Y-%m-%d")} #{event[:ip]}",
|
93
96
|
event[:url],
|
94
97
|
event[:controller],
|
95
98
|
event[:html_verb],
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<table class="table unstriped command-invocation">
|
2
|
+
<tbody>
|
3
|
+
<tr>
|
4
|
+
<th>CLI Command</th>
|
5
|
+
<td><code><%= data[:command] %></code></td>
|
6
|
+
</tr>
|
7
|
+
<tr>
|
8
|
+
<th>Input file</th>
|
9
|
+
<td><code><%= (data[:log_file] || "stdin") %></code></td>
|
10
|
+
</tr>
|
11
|
+
<tr>
|
12
|
+
<th>Ignore crawlers</th>
|
13
|
+
<td><code><%= options[:ignore_crawlers] %></code></td></tr>
|
14
|
+
<tr>
|
15
|
+
<th>Only crawlers</th>
|
16
|
+
<td><code><%= options[:only_crawlers] %></code></td>
|
17
|
+
</tr>
|
18
|
+
<tr>
|
19
|
+
<th>No selfpoll</th>
|
20
|
+
<td><code><%= options[:no_selfpoll] %></code></td>
|
21
|
+
</tr>
|
22
|
+
<tr>
|
23
|
+
<th>Filter by date</th>
|
24
|
+
<td>
|
25
|
+
<code><%= (options[:from_date] != nil or options[:to_date] != nil) %></code>
|
26
|
+
</td>
|
27
|
+
</tr>
|
28
|
+
</tbody>
|
29
|
+
</table>
|
@@ -4,7 +4,7 @@ def slugify string
|
|
4
4
|
end
|
5
5
|
%>
|
6
6
|
|
7
|
-
<table id="<%= slugify(title || "")
|
7
|
+
<table id="<%= slugify(title || "") %>-table" class="table unstriped data-table <%= slugify(title || "") %>">
|
8
8
|
<thead>
|
9
9
|
<tr>
|
10
10
|
<% header.each do |heading| %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<table class="table unstriped performance">
|
2
|
+
<tbody>
|
3
|
+
<tr>
|
4
|
+
<th>Analysis started at</th>
|
5
|
+
<td><%= data[:started_at].to_s %></td>
|
6
|
+
</tr>
|
7
|
+
<tr>
|
8
|
+
<th>Analysis ended at</th>
|
9
|
+
<td><%= data[:ended_at].to_s %></td>
|
10
|
+
</tr>
|
11
|
+
<tr>
|
12
|
+
<th>Duration</th>
|
13
|
+
<td><%= "%02d:%02d" % [data[:duration] / 60, data[:duration] % 60] %></td>
|
14
|
+
</tr>
|
15
|
+
<tr>
|
16
|
+
<th>Events</th>
|
17
|
+
<td><%= data[:log_size] %></td>
|
18
|
+
</tr>
|
19
|
+
<tr>
|
20
|
+
<th>Parsed Events/sec</th>
|
21
|
+
<td><%= "%.2f" % (data[:log_size] / data[:duration]) %></td></tr>
|
22
|
+
</tbody>
|
23
|
+
</table>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%=
|
2
|
+
table = Terminal::Table.new rows: [ ["Analysis started at", data[:started_at].to_s ],
|
3
|
+
["Analysis ended at", data[:ended_at].to_s ],
|
4
|
+
["Duration", "%02d:%02d" % [data[:duration] / 60, data[:duration] % 60] ],
|
5
|
+
["Events", "%9d" % data[:log_size] ],
|
6
|
+
["Parsed events/sec", "%.2f" % (data[:log_size] / data[:duration]) ] ]
|
7
|
+
table.align_column(2, :right)
|
8
|
+
table
|
9
|
+
%>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<table class="table unstriped summary">
|
2
|
+
<tr>
|
3
|
+
<th>Input file</th>
|
4
|
+
<td><b><%= (data[:log_file] || "stdin") %></b></td>
|
5
|
+
</tr>
|
6
|
+
<tr>
|
7
|
+
<th class="period">Period Analyzed</th>
|
8
|
+
<td class="period">
|
9
|
+
<%= data[:first_day_in_analysis] %>
|
10
|
+
--
|
11
|
+
<%= data[:last_day_in_analysis] %>
|
12
|
+
</td>
|
13
|
+
</tr>
|
14
|
+
<tr>
|
15
|
+
<th class="days">Days </th>
|
16
|
+
<td class="days"><%= data[:total_days_in_analysis] %></td>
|
17
|
+
</tr>
|
18
|
+
<tr>
|
19
|
+
<th class="hits">Hits</th>
|
20
|
+
<td class="hits"><%= data[:total_hits] %></td>
|
21
|
+
</tr>
|
22
|
+
<tr>
|
23
|
+
<th class="unique-visits">Unique Visits</th>
|
24
|
+
<td class="unique-visits"><%= data[:total_unique_visits] %></td>
|
25
|
+
</tr>
|
26
|
+
<tr>
|
27
|
+
<th class="avg-hits-per-unique-visits">Unique Visits</th>
|
28
|
+
<td class="avg-hits-per-unique-visits"><%= data[:total_hits] / data[:total_unique_visits] %></td>
|
29
|
+
</tr>
|
30
|
+
<tr>
|
31
|
+
<th class="tx">Tx</th>
|
32
|
+
<td class="tx"><%= data[:total_size] %></td>
|
33
|
+
</tr>
|
34
|
+
</table>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%=
|
2
|
+
table = Terminal::Table.new rows: [ ["Input File", data[:log_file] || "stdin" ],
|
3
|
+
["Period Analyzed", "#{data[:first_day_in_analysis]} -- #{data[:last_day_in_analysis]}" ],
|
4
|
+
["Days", data[:total_days_in_analysis] ],
|
5
|
+
["Events", data[:events] ],
|
6
|
+
["Unique Visits", data[:total_unique_visits] ],
|
7
|
+
["Avg. Events per Visit", data[:events] / data[:total_unique_visits] ]
|
8
|
+
]
|
9
|
+
table
|
10
|
+
%>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<table class="table unstriped log-structure">
|
2
|
+
<tbody>
|
3
|
+
<tr>
|
4
|
+
<th>Input file</th>
|
5
|
+
<td><b><%= (data[:log_file] || "stdin") %></b></td>
|
6
|
+
</tr>
|
7
|
+
<tr>
|
8
|
+
<th>Period in Log</th>
|
9
|
+
<td><%= data[:first_day] %> -- <%= data[:last_day] %></td>
|
10
|
+
</tr>
|
11
|
+
<tr>
|
12
|
+
<th>Total days</th>
|
13
|
+
<td><%= data[:total_days] %></td>
|
14
|
+
</tr>
|
15
|
+
<tr>
|
16
|
+
<th>Log size</th>
|
17
|
+
<td><%= data[:log_size] %></td>
|
18
|
+
</tr>
|
19
|
+
<tr>
|
20
|
+
<th>Self poll entries</th>
|
21
|
+
<td><%= data[:selfpolls_size] %></td>
|
22
|
+
</tr>
|
23
|
+
<tr>
|
24
|
+
<th>Crawlers</th>
|
25
|
+
<td><%= data[:crawlers_size] %></td>
|
26
|
+
</tr>
|
27
|
+
<tr>
|
28
|
+
<th>Entries considered</th>
|
29
|
+
<td><%= data[:total_hits] %></td>
|
30
|
+
</tr>
|
31
|
+
</tbody>
|
32
|
+
</table>
|