log_sense 1.4.1 → 1.5.0
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/CHANGELOG.org +15 -0
- data/Gemfile.lock +4 -4
- data/README.org +20 -7
- data/Rakefile +17 -3
- data/exe/log_sense +13 -3
- data/ip_locations/dbip-country-lite.sqlite3 +0 -0
- data/lib/log_sense/apache_data_cruncher.rb +17 -19
- data/lib/log_sense/apache_log_line_parser.rb +1 -1
- data/lib/log_sense/apache_log_parser.rb +1 -1
- data/lib/log_sense/emitter.rb +72 -15
- data/lib/log_sense/ip_locator.rb +26 -19
- data/lib/log_sense/options_parser.rb +5 -0
- data/lib/log_sense/rails_data_cruncher.rb +3 -0
- data/lib/log_sense/templates/_navigation.html.erb +21 -0
- data/lib/log_sense/templates/_output_table.txt.erb +3 -2
- data/lib/log_sense/templates/_summary.html.erb +1 -1
- data/lib/log_sense/templates/_summary.txt.erb +1 -1
- data/lib/log_sense/templates/_warning.txt.erb +1 -0
- data/lib/log_sense/templates/apache.html.erb +14 -134
- data/lib/log_sense/templates/apache.txt.erb +2 -15
- data/lib/log_sense/templates/rails.html.erb +13 -37
- data/lib/log_sense/templates/rails.txt.erb +5 -6
- data/lib/log_sense/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0128717b2ba709bc5dfbb7b755762a757c575ad4307159910cc894aaa3b88f42'
|
4
|
+
data.tar.gz: e05054b8eee79a439f5b077e60bc0d95a3e7706c550853333d7d631c458abf91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d9e3dc495f7479292ae96d1bf6298f531258cae74df47ff705aea9613880b0d50aa6a19328f70685484ad1c606bd12a8fb7c87632fe5c9cbefefe4893d9bb4d
|
7
|
+
data.tar.gz: b417049bcc119ed82ab4c33d007e15804ba85b2485308ca28448fba512814fc5e8b8b215310bfb5c8108fcdc87292322eefc6826b18718fcbc7fced29eea77cb
|
data/CHANGELOG.org
CHANGED
@@ -2,6 +2,21 @@
|
|
2
2
|
#+AUTHOR: Adolfo Villafiorita
|
3
3
|
#+STARTUP: showall
|
4
4
|
|
5
|
+
* 1.5.0
|
6
|
+
|
7
|
+
- [User] Present Unique Visits / day as integer
|
8
|
+
- [User] Added Country and Streaks report for rails
|
9
|
+
- [User] Changed Streak report in Apache
|
10
|
+
|
11
|
+
- [Gem] Updated DBIP
|
12
|
+
- [Gem] Updated Bundle
|
13
|
+
|
14
|
+
- [Code] Refactored all reports, so that they are specified
|
15
|
+
in the same way
|
16
|
+
- [Code] Refactor warning message in textual reports
|
17
|
+
- [Code] Build HTML menu for report specification
|
18
|
+
- [Code] Various refactoring passes on the code
|
19
|
+
|
5
20
|
* 1.4.1
|
6
21
|
|
7
22
|
- [User] New textual report for Apache
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
log_sense (1.
|
4
|
+
log_sense (1.4.2)
|
5
5
|
browser
|
6
6
|
ipaddr
|
7
7
|
iso_country_codes
|
@@ -13,9 +13,9 @@ GEM
|
|
13
13
|
specs:
|
14
14
|
browser (5.3.1)
|
15
15
|
byebug (11.1.3)
|
16
|
-
ipaddr (1.2.
|
16
|
+
ipaddr (1.2.4)
|
17
17
|
iso_country_codes (0.7.8)
|
18
|
-
minitest (5.
|
18
|
+
minitest (5.15.0)
|
19
19
|
rake (12.3.3)
|
20
20
|
sqlite3 (1.4.2)
|
21
21
|
terminal-table (3.0.2)
|
@@ -32,4 +32,4 @@ DEPENDENCIES
|
|
32
32
|
rake (~> 12.0)
|
33
33
|
|
34
34
|
BUNDLED WITH
|
35
|
-
2.
|
35
|
+
2.3.3
|
data/README.org
CHANGED
@@ -19,8 +19,6 @@ LogSense reports the following data:
|
|
19
19
|
- OS, browsers, and devices
|
20
20
|
- IP Country location, thanks to the DPIP lite country DB
|
21
21
|
- Streaks: resources accessed by a given IP over time
|
22
|
-
- Potential attacks: access to resources which are not meant to be
|
23
|
-
served by a web server serving static websites
|
24
22
|
- Performance of Rails requests
|
25
23
|
|
26
24
|
Filters from the command line allow to analyze specific periods and
|
@@ -33,6 +31,18 @@ And, of course, the compulsory screenshot:
|
|
33
31
|
#+ATTR_HTML: :width 80%
|
34
32
|
[[file:./apache-screenshot.png]]
|
35
33
|
|
34
|
+
|
35
|
+
* An important word of warning
|
36
|
+
|
37
|
+
[[https://owasp.org/www-community/attacks/Log_Injection][Log poisoning]] is a technique whereby attackers send requests with invalidated
|
38
|
+
user input to forge log entries or inject malicious content into the logs.
|
39
|
+
|
40
|
+
log_sense sanitizes entries of HTML reports, to try and protect from log
|
41
|
+
poisoning. *Log entries and URLs in SQLite3, however, are not sanitized*:
|
42
|
+
they are stored and read from the log. This is not, in general, an issue,
|
43
|
+
unless you use the data from SQLite in environments in which URLs can be
|
44
|
+
opened or code executed.
|
45
|
+
|
36
46
|
* Motivation
|
37
47
|
|
38
48
|
LogSense moves along the lines of tools such as [[https://goaccess.io/][GoAccess]] (which
|
@@ -54,6 +64,7 @@ generated files are then made available on a private area on the web.
|
|
54
64
|
gem install log_sense
|
55
65
|
#+end_src
|
56
66
|
|
67
|
+
|
57
68
|
* Usage
|
58
69
|
|
59
70
|
#+begin_src bash :results raw output :wrap example
|
@@ -73,10 +84,11 @@ generated files are then made available on a private area on the web.
|
|
73
84
|
-w, --width=WIDTH Maximum width of URL and description columns in text reports
|
74
85
|
-c, --crawlers=POLICY Decide what to do with crawlers (applies to Apache Logs)
|
75
86
|
-n, --no-selfpolls Ignore self poll entries (requests from ::1; applies to Apache Logs)
|
87
|
+
--verbose Inform about progress (prints to STDERR)
|
76
88
|
-v, --version Prints version information
|
77
89
|
-h, --help Prints this help
|
78
90
|
|
79
|
-
This is version 1.
|
91
|
+
This is version 1.5.0
|
80
92
|
|
81
93
|
Output formats
|
82
94
|
rails parsing can produce the following outputs:
|
@@ -96,6 +108,7 @@ log_sense -f apache -i access.log -t txt > access-data.txt
|
|
96
108
|
log_sense -f rails -i production.log -t html -o performance.txt
|
97
109
|
#+end_example
|
98
110
|
|
111
|
+
|
99
112
|
* Change Log
|
100
113
|
|
101
114
|
See the [[file:CHANGELOG.org][CHANGELOG]] file.
|
@@ -110,8 +123,8 @@ Concerning the outputs:
|
|
110
123
|
- HTML reports use [[https://get.foundation/][Zurb Foundation]], [[https://www.datatables.net/][Data Tables]], and [[https://vega.github.io/vega-lite/][Vega Light]], which
|
111
124
|
are all downloaded from a CDN
|
112
125
|
- The textual format is compatible with [[https://orgmode.org/][Org Mode]] and can be further
|
113
|
-
processed to any format [[https://orgmode.org/][Org Mode]] can be exported to
|
114
|
-
and PDF
|
126
|
+
processed to any format [[https://orgmode.org/][Org Mode]] can be exported to, including HTML
|
127
|
+
and PDF, with the word of warning in the section above.
|
115
128
|
|
116
129
|
* Author and Contributors
|
117
130
|
|
@@ -119,8 +132,8 @@ Concerning the outputs:
|
|
119
132
|
|
120
133
|
* Known Bugs
|
121
134
|
|
122
|
-
No known bugs; an unknown number of unknown bugs.
|
123
|
-
|
135
|
+
No known bugs; an unknown number of unknown bugs. (See the open issues for
|
136
|
+
the known bugs.)
|
124
137
|
|
125
138
|
* License
|
126
139
|
|
data/Rakefile
CHANGED
@@ -9,7 +9,21 @@ end
|
|
9
9
|
require_relative './lib/log_sense/ip_locator.rb'
|
10
10
|
|
11
11
|
desc "Convert Geolocation DB to sqlite"
|
12
|
-
task :dbip_to_sqlite3, [:
|
13
|
-
filename = args[:
|
14
|
-
|
12
|
+
task :dbip_to_sqlite3, [:year_month] do |tasks, args|
|
13
|
+
filename = "./ip_locations/dbip-country-lite-#{args[:year_month]}.csv"
|
14
|
+
|
15
|
+
if !File.exist? filename
|
16
|
+
puts "Error. Could not find: #{filename}"
|
17
|
+
puts
|
18
|
+
puts 'I see the following files:'
|
19
|
+
puts Dir.glob("ip_locations/dbip-country-lite*").map { |x| "- #{x}\n" }
|
20
|
+
puts ''
|
21
|
+
puts '1. Download (if necessary) a more recent version from: https://db-ip.com/db/download/ip-to-country-lite'
|
22
|
+
puts '2. Save downloaded file to ip_locations/'
|
23
|
+
puts '3. Relaunch with YYYY-MM'
|
24
|
+
|
25
|
+
exit
|
26
|
+
else
|
27
|
+
LogSense::IpLocator::dbip_to_sqlite filename
|
28
|
+
end
|
15
29
|
end
|
data/exe/log_sense
CHANGED
@@ -12,7 +12,7 @@ require 'log_sense.rb'
|
|
12
12
|
@output_file = @options[:output_file]
|
13
13
|
|
14
14
|
if ARGV.map { |x| File.exist?(x) }.include?(false)
|
15
|
-
|
15
|
+
$stderr.puts "Error: input file(s) '#{ARGV.reject { |x| File.exist(x) }.join(', ')}' do not exist"
|
16
16
|
exit 1
|
17
17
|
end
|
18
18
|
@input_files = ARGV.empty? ? [$stdin] : ARGV.map { |x| File.open(x, 'r') }
|
@@ -31,35 +31,45 @@ when 'rails'
|
|
31
31
|
parser_klass = LogSense::RailsLogParser
|
32
32
|
cruncher_klass = LogSense::RailsDataCruncher
|
33
33
|
else
|
34
|
-
|
34
|
+
$stderr.puts "Error: input format #{@options[:input_format]} not understood."
|
35
35
|
exit 1
|
36
36
|
end
|
37
37
|
|
38
|
+
$stderr.puts "Parsing input files..." if @options[:verbose]
|
38
39
|
@db = parser_klass.parse @input_files
|
39
40
|
|
40
41
|
if @options[:output_format] == 'sqlite'
|
42
|
+
$stderr.puts "Saving to SQLite3..." if @options[:verbose]
|
41
43
|
ddb = SQLite3::Database.new(@output_file || 'db.sqlite3')
|
42
44
|
b = SQLite3::Backup.new(ddb, 'main', @db, 'main')
|
43
45
|
b.step(-1) #=> DONE
|
44
46
|
b.finish
|
45
47
|
else
|
48
|
+
$stderr.puts "Aggregating data..." if @options[:verbose]
|
46
49
|
@data = cruncher_klass.crunch @db, @options
|
50
|
+
|
51
|
+
$stderr.puts "Geolocating..." if @options[:verbose]
|
47
52
|
@data = LogSense::IpLocator.geolocate @data
|
48
53
|
|
54
|
+
$stderr.puts "Grouping by country..." if @options[:verbose]
|
55
|
+
country_col = @data[:ips][0].size - 1
|
56
|
+
@data[:countries] = @data[:ips].group_by { |x| x[country_col] }
|
57
|
+
|
49
58
|
@ended_at = Time.now
|
50
59
|
@duration = @ended_at - @started_at
|
51
60
|
|
52
61
|
@data = @data.merge({
|
53
62
|
command: @command_line,
|
63
|
+
filenames: ARGV,
|
54
64
|
log_files: @input_files,
|
55
65
|
started_at: @started_at,
|
56
66
|
ended_at: @ended_at,
|
57
67
|
duration: @duration,
|
58
68
|
width: @options[:width]
|
59
69
|
})
|
60
|
-
|
61
70
|
#
|
62
71
|
# Emit Output
|
63
72
|
#
|
73
|
+
$stderr.puts "Emitting..." if @options[:verbose]
|
64
74
|
puts LogSense::Emitter.emit @data, @options
|
65
75
|
end
|
Binary file
|
@@ -17,15 +17,15 @@ module LogSense
|
|
17
17
|
@total_days = 0
|
18
18
|
@total_days = (@last_day - @first_day).to_i if @first_day && @last_day
|
19
19
|
|
20
|
-
@source_files = db.execute
|
20
|
+
@source_files = db.execute 'SELECT distinct(source_file) from LogLine'
|
21
21
|
|
22
|
-
@log_size = db.execute
|
22
|
+
@log_size = db.execute 'SELECT count(datetime) from LogLine'
|
23
23
|
@log_size = @log_size[0][0]
|
24
24
|
|
25
25
|
@selfpolls_size = db.execute "SELECT count(datetime) from LogLine where ip == '::1'"
|
26
26
|
@selfpolls_size = @selfpolls_size[0][0]
|
27
27
|
|
28
|
-
@crawlers_size = db.execute
|
28
|
+
@crawlers_size = db.execute 'SELECT count(datetime) from LogLine where bot == 1'
|
29
29
|
@crawlers_size = @crawlers_size[0][0]
|
30
30
|
|
31
31
|
@first_day_requested = options[:from_date]
|
@@ -35,7 +35,7 @@ module LogSense
|
|
35
35
|
@last_day_in_analysis = date_intersect options[:to_date], @last_day, :min
|
36
36
|
|
37
37
|
@total_days_in_analysis = 0
|
38
|
-
if @first_day_in_analysis
|
38
|
+
if @first_day_in_analysis && @last_day_in_analysis
|
39
39
|
@total_days_in_analysis = (@last_day_in_analysis - @first_day_in_analysis).to_i
|
40
40
|
end
|
41
41
|
|
@@ -45,24 +45,24 @@ module LogSense
|
|
45
45
|
filter = [
|
46
46
|
(options[:from_date] ? "date(datetime) >= '#{options[:from_date]}'" : nil),
|
47
47
|
(options[:to_date] ? "date(datetime) <= '#{options[:to_date]}'" : nil),
|
48
|
-
(options[:only_crawlers] ?
|
49
|
-
(options[:ignore_crawlers] ?
|
48
|
+
(options[:only_crawlers] ? 'bot == 1' : nil),
|
49
|
+
(options[:ignore_crawlers] ? 'bot == 0' : nil),
|
50
50
|
(options[:no_selfpolls] ? "ip != '::1'" : nil),
|
51
|
-
|
51
|
+
'true'
|
52
52
|
].compact.join " and "
|
53
53
|
|
54
54
|
mega = 1024 * 1024
|
55
55
|
giga = mega * 1024
|
56
56
|
tera = giga * 1024
|
57
|
-
|
57
|
+
|
58
58
|
# in alternative to sum(size)
|
59
59
|
human_readable_size = <<-EOS
|
60
|
-
CASE
|
60
|
+
CASE
|
61
61
|
WHEN sum(size) < 1024 THEN sum(size) || ' B'
|
62
62
|
WHEN sum(size) >= 1024 AND sum(size) < (#{mega}) THEN ROUND((CAST(sum(size) AS REAL) / 1024), 2) || ' KB'
|
63
63
|
WHEN sum(size) >= (#{mega}) AND sum(size) < (#{giga}) THEN ROUND((CAST(sum(size) AS REAL) / (#{mega})), 2) || ' MB'
|
64
64
|
WHEN sum(size) >= (#{giga}) AND sum(size) < (#{tera}) THEN ROUND((CAST(sum(size) AS REAL) / (#{giga})), 2) || ' GB'
|
65
|
-
WHEN sum(size) >= (#{tera}) THEN ROUND((CAST(sum(size) AS REAL) / (#{tera})), 2) || ' TB'
|
65
|
+
WHEN sum(size) >= (#{tera}) THEN ROUND((CAST(sum(size) AS REAL) / (#{tera})), 2) || ' TB'
|
66
66
|
END AS size
|
67
67
|
EOS
|
68
68
|
|
@@ -117,20 +117,19 @@ module LogSense
|
|
117
117
|
|
118
118
|
@ips = db.execute "SELECT ip, count(ip), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where #{filter} group by ip order by count(ip) desc limit #{options[:limit]}"
|
119
119
|
|
120
|
-
@streaks = db.execute
|
120
|
+
@streaks = db.execute 'SELECT ip, substr(datetime, 1, 10), path from LogLine order by ip, datetime'
|
121
121
|
data = {}
|
122
122
|
|
123
|
-
|
124
|
-
var_as_symbol = variable.to_s[1
|
125
|
-
data[var_as_symbol] =
|
123
|
+
instance_variables.each do |variable|
|
124
|
+
var_as_symbol = variable.to_s[1..].to_sym
|
125
|
+
data[var_as_symbol] = instance_variable_get(variable)
|
126
126
|
end
|
127
|
+
|
127
128
|
data
|
128
129
|
end
|
129
130
|
|
130
|
-
|
131
|
-
|
132
|
-
def self.date_intersect date1, date2, method
|
133
|
-
if date1 and date2
|
131
|
+
def self.date_intersect(date1, date2, method)
|
132
|
+
if date1 && date2
|
134
133
|
[date1, date2].send(method)
|
135
134
|
elsif date1
|
136
135
|
date1
|
@@ -140,4 +139,3 @@ module LogSense
|
|
140
139
|
end
|
141
140
|
end
|
142
141
|
end
|
143
|
-
|
data/lib/log_sense/emitter.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'terminal-table'
|
2
3
|
require 'json'
|
3
4
|
require 'erb'
|
4
5
|
require 'ostruct'
|
5
|
-
|
6
6
|
module LogSense
|
7
7
|
#
|
8
8
|
# Emit Data
|
@@ -31,9 +31,7 @@ module LogSense
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
def self.render(template, vars)
|
34
|
+
def self.render(template, vars = {})
|
37
35
|
@template = File.join(File.dirname(__FILE__), 'templates', "_#{template}")
|
38
36
|
erb_template = File.read @template
|
39
37
|
ERB.new(erb_template).result(OpenStruct.new(vars).instance_eval { binding })
|
@@ -42,11 +40,11 @@ module LogSense
|
|
42
40
|
def self.escape_javascript(string)
|
43
41
|
js_escape_map = {
|
44
42
|
'<' => '<',
|
45
|
-
'</' => '<
|
46
|
-
'\\' => '\\\\',
|
43
|
+
'</' => '</',
|
47
44
|
'\r\n' => '\\r\\n',
|
48
45
|
'\n' => '\\n',
|
49
46
|
'\r' => '\\r',
|
47
|
+
'\\' => ' \\\\',
|
50
48
|
'"' => ' \\"',
|
51
49
|
"'" => " \\'",
|
52
50
|
'`' => ' \\`',
|
@@ -73,7 +71,9 @@ module LogSense
|
|
73
71
|
# - width width to set
|
74
72
|
def self.shorten(data, heading, width)
|
75
73
|
# indexes of columns which have to be shortened
|
76
|
-
|
74
|
+
keywords = %w[URL Referers Description Path]
|
75
|
+
to_shorten = keywords.map { |x| heading.index x }.compact
|
76
|
+
|
77
77
|
if width.nil? || to_shorten.empty? || data[0].nil?
|
78
78
|
data
|
79
79
|
else
|
@@ -96,7 +96,7 @@ module LogSense
|
|
96
96
|
# column_alignment: specification of column alignments (works for txt reports)
|
97
97
|
# vega_spec: specifications for Vega output
|
98
98
|
# datatable_options: specific options for datatable
|
99
|
-
def self.apache_report_specification(data)
|
99
|
+
def self.apache_report_specification(data = {})
|
100
100
|
[
|
101
101
|
{ title: 'Daily Distribution',
|
102
102
|
header: %w[Day DOW Hits Visits Size],
|
@@ -326,6 +326,19 @@ module LogSense
|
|
326
326
|
column_alignment: %i[left right right right left],
|
327
327
|
rows: data[:ips]
|
328
328
|
},
|
329
|
+
{
|
330
|
+
title: 'Countries',
|
331
|
+
header: %w[Country Hits Visits IPs],
|
332
|
+
column_alignment: %i[left right right left],
|
333
|
+
rows: data[:countries]&.map do |k, v|
|
334
|
+
[
|
335
|
+
k,
|
336
|
+
v.map { |x| x[1] }.inject(&:+),
|
337
|
+
v.map { |x| x[2] }.inject(&:+),
|
338
|
+
v.map { |x| x[0] }.join(' ')
|
339
|
+
]
|
340
|
+
end
|
341
|
+
},
|
329
342
|
{
|
330
343
|
title: 'Referers',
|
331
344
|
header: %w[Referers Hits Visits Size],
|
@@ -333,10 +346,27 @@ module LogSense
|
|
333
346
|
rows: data[:referers],
|
334
347
|
col: 'small-12 cell'
|
335
348
|
},
|
349
|
+
{
|
350
|
+
title: 'Streaks',
|
351
|
+
report: :html,
|
352
|
+
header: ['IP', 'Date', 'Total HTML', 'Total Other', 'HTML', 'Other'],
|
353
|
+
column_alignment: %i[left left right right left left],
|
354
|
+
rows: data[:streaks]&.group_by { |x| [x[0], x[1]] }&.map do |k, v|
|
355
|
+
[
|
356
|
+
k[0],
|
357
|
+
k[1],
|
358
|
+
v.map { |x| x[2] }.compact.select { |x| x.match(/\.html?$/) }.size,
|
359
|
+
v.map { |x| x[2] }.compact.reject { |x| x.match(/\.html?$/) }.size,
|
360
|
+
v.map { |x| x[2] }.compact.select { |x| x.match(/\.html?$/) }.join(' ■ '),
|
361
|
+
v.map { |x| x[2] }.compact.reject { |x| x.match(/\.html?$/) }.join(' ■ ')
|
362
|
+
]
|
363
|
+
end,
|
364
|
+
col: 'small-12 cell'
|
365
|
+
}
|
336
366
|
]
|
337
367
|
end
|
338
368
|
|
339
|
-
def self.rails_report_specification(data)
|
369
|
+
def self.rails_report_specification(data = {})
|
340
370
|
[
|
341
371
|
{
|
342
372
|
title: "Daily Distribution",
|
@@ -469,27 +499,54 @@ module LogSense
|
|
469
499
|
header: %w[Date IP URL Description Log ID],
|
470
500
|
column_alignment: %i[left left left left left],
|
471
501
|
rows: data[:fatal],
|
472
|
-
col:
|
502
|
+
col: 'small-12 cell'
|
473
503
|
},
|
474
504
|
{
|
475
|
-
title:
|
505
|
+
title: 'Internal Server Errors',
|
476
506
|
header: %w[Date Status IP URL Description Log ID],
|
477
507
|
column_alignment: %i[left left left left left left],
|
478
508
|
rows: data[:internal_server_error],
|
479
|
-
col:
|
509
|
+
col: 'small-12 cell'
|
480
510
|
},
|
481
511
|
{
|
482
|
-
title:
|
512
|
+
title: 'Errors',
|
483
513
|
header: %w[Log ID Context Description Count],
|
484
514
|
column_alignment: %i[left left left left],
|
485
515
|
rows: data[:error],
|
486
|
-
col:
|
516
|
+
col: 'small-12 cell'
|
487
517
|
},
|
488
518
|
{
|
489
|
-
title:
|
519
|
+
title: 'IPs',
|
490
520
|
header: %w[IPs Hits Country],
|
491
521
|
column_alignment: %i[left right left],
|
492
522
|
rows: data[:ips]
|
523
|
+
},
|
524
|
+
{
|
525
|
+
title: 'Countries',
|
526
|
+
header: %w[Country Hits IPs],
|
527
|
+
column_alignment: %i[left right left],
|
528
|
+
rows: data[:countries]&.map do |k, v|
|
529
|
+
[
|
530
|
+
k,
|
531
|
+
v.map { |x| x[1] }.inject(&:+),
|
532
|
+
v.map { |x| x[0] }.join(' ■ ')
|
533
|
+
]
|
534
|
+
end
|
535
|
+
},
|
536
|
+
{
|
537
|
+
title: 'Streaks',
|
538
|
+
report: :html,
|
539
|
+
header: %w[IP Date Total Resources],
|
540
|
+
column_alignment: %i[left left right right left left],
|
541
|
+
rows: data[:streaks]&.group_by { |x| [x[0], x[1]] }&.map do |k, v|
|
542
|
+
[
|
543
|
+
k[0],
|
544
|
+
k[1],
|
545
|
+
v.size,
|
546
|
+
v.map { |x| x[2] }.join(' ■ ')
|
547
|
+
]
|
548
|
+
end,
|
549
|
+
col: 'small-12 cell'
|
493
550
|
}
|
494
551
|
]
|
495
552
|
end
|
data/lib/log_sense/ip_locator.rb
CHANGED
@@ -4,19 +4,22 @@ require 'ipaddr'
|
|
4
4
|
require 'iso_country_codes'
|
5
5
|
|
6
6
|
module LogSense
|
7
|
+
#
|
8
|
+
# Populate table of IP Locations from dbip-country-lite
|
9
|
+
#
|
7
10
|
module IpLocator
|
8
|
-
DB_FILE = File.join(File.dirname(__FILE__),
|
11
|
+
DB_FILE = File.join(File.dirname(__FILE__), '..', '..', 'ip_locations', 'dbip-country-lite.sqlite3')
|
9
12
|
|
10
|
-
def self.dbip_to_sqlite
|
11
|
-
db = SQLite3::Database.new
|
12
|
-
db.execute
|
13
|
+
def self.dbip_to_sqlite(db_location)
|
14
|
+
db = SQLite3::Database.new ':memory:'
|
15
|
+
db.execute 'CREATE TABLE ip_location (
|
13
16
|
from_ip_n INTEGER,
|
14
17
|
from_ip TEXT,
|
15
18
|
to_ip TEXT,
|
16
19
|
country_code TEXT
|
17
|
-
)
|
20
|
+
)'
|
18
21
|
|
19
|
-
ins = db.prepare
|
22
|
+
ins = db.prepare 'INSERT INTO ip_location(from_ip_n, from_ip, to_ip, country_code) values (?, ?, ?, ?)'
|
20
23
|
CSV.foreach(db_location) do |row|
|
21
24
|
ip = IPAddr.new row[0]
|
22
25
|
ins.execute(ip.to_i, row[0], row[1], row[2])
|
@@ -33,29 +36,33 @@ module LogSense
|
|
33
36
|
SQLite3::Database.new DB_FILE
|
34
37
|
end
|
35
38
|
|
36
|
-
def self.locate_ip
|
37
|
-
return
|
39
|
+
def self.locate_ip(ip, db)
|
40
|
+
return unless ip
|
38
41
|
|
39
|
-
|
40
|
-
res = db.execute "SELECT * FROM ip_location where from_ip_n <= #{ip_n} order by from_ip_n desc limit 1"
|
42
|
+
query = db.prepare 'SELECT * FROM ip_location where from_ip_n <= ? order by from_ip_n desc limit 1'
|
41
43
|
begin
|
42
|
-
|
43
|
-
|
44
|
-
|
44
|
+
ip_n = IPAddr.new(ip).to_i
|
45
|
+
result_set = query.execute ip_n
|
46
|
+
country_code = result_set.map { |x| x[3] }[0]
|
47
|
+
IsoCountryCodes.find(country_code).name
|
48
|
+
rescue IPAddr::InvalidAddressError
|
49
|
+
'INVALID IP'
|
50
|
+
rescue IsoCountryCodes::UnknownCodeError
|
51
|
+
country_code
|
45
52
|
end
|
46
53
|
end
|
47
54
|
|
48
55
|
#
|
49
56
|
# add country code to data[:ips]
|
50
57
|
#
|
51
|
-
def self.geolocate
|
52
|
-
@location_db = IpLocator
|
53
|
-
|
54
|
-
|
55
|
-
|
58
|
+
def self.geolocate(data)
|
59
|
+
@location_db = IpLocator.load_db
|
60
|
+
|
61
|
+
data[:ips].each do |line|
|
62
|
+
country_code = IpLocator.locate_ip line[0], @location_db
|
63
|
+
line << country_code
|
56
64
|
end
|
57
65
|
data
|
58
66
|
end
|
59
|
-
|
60
67
|
end
|
61
68
|
end
|
@@ -59,6 +59,10 @@ module LogSense
|
|
59
59
|
args[:no_selfpoll] = true
|
60
60
|
end
|
61
61
|
|
62
|
+
opts.on('--verbose', 'Inform about progress (prints to STDERR)') do
|
63
|
+
args[:verbose] = true
|
64
|
+
end
|
65
|
+
|
62
66
|
opts.on('-v', '--version', 'Prints version information') do
|
63
67
|
puts "log_sense version #{LogSense::VERSION}"
|
64
68
|
puts 'Copyright (C) 2021 Shair.Tech'
|
@@ -96,6 +100,7 @@ module LogSense
|
|
96
100
|
args[:ignore_crawlers] ||= false
|
97
101
|
args[:only_crawlers] ||= false
|
98
102
|
args[:no_selfpoll] ||= false
|
103
|
+
args[:verbose] ||= false
|
99
104
|
|
100
105
|
args
|
101
106
|
end
|
@@ -104,6 +104,9 @@ module LogSense
|
|
104
104
|
|
105
105
|
@ips = db.execute "SELECT ip, count(ip) from Event where #{filter} group by ip order by count(ip) desc limit #{options[:limit]}"
|
106
106
|
|
107
|
+
@streaks = db.execute 'SELECT ip, substr(started_at, 1, 10), url from Event order by ip, started_at'
|
108
|
+
data = {}
|
109
|
+
|
107
110
|
@performance = db.execute "SELECT distinct(controller), count(controller), printf(\"%.2f\", min(duration_total_ms)), printf(\"%.2f\", avg(duration_total_ms)), printf(\"%.2f\", max(duration_total_ms)) from Event group by controller order by controller"
|
108
111
|
|
109
112
|
@fatal = db.execute ("SELECT strftime(\"%Y-%m-%d %H:%M\", started_at), ip, url, error.description, event.log_id FROM Event JOIN Error ON event.log_id == error.log_id WHERE exit_status == 'F'") || [[]]
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<nav>
|
2
|
+
<h2>Navigation</h2>
|
3
|
+
<ul class="no-bullet">
|
4
|
+
<% (["Summary", "Log Structure"] +
|
5
|
+
menus +
|
6
|
+
["Command Invocation", "Performance"]).each do |item| %>
|
7
|
+
<li class="nav-item">
|
8
|
+
<a href="#<%= Emitter::slugify item %>" data-close>
|
9
|
+
<%= item %>
|
10
|
+
</a>
|
11
|
+
</li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
14
|
+
|
15
|
+
<p>
|
16
|
+
Generated by
|
17
|
+
<a href="https://github.com/avillafiorita/log_sense">LogSense</a> <br />
|
18
|
+
on <%= DateTime.now.strftime("%Y-%m-%d %H:%M") %>.<br />
|
19
|
+
<a href='https://db-ip.com'>IP Geolocation by DB-IP</a>
|
20
|
+
</p>
|
21
|
+
</nav>
|
@@ -6,8 +6,9 @@ shortened = Emitter::shorten(report[:rows], report[:header], data[:width])
|
|
6
6
|
table = Terminal::Table.new headings: report[:header], rows: shortened
|
7
7
|
table.style = { border_i: "|" }
|
8
8
|
columns = report[:header].size - 1
|
9
|
-
(0..columns).map
|
10
|
-
|
9
|
+
(0..columns).map do |i|
|
10
|
+
table.align_column(i, report[:column_alignment][i] || :left)
|
11
|
+
end
|
11
12
|
# return it
|
12
13
|
table
|
13
14
|
%>
|
@@ -18,7 +18,7 @@
|
|
18
18
|
</li>
|
19
19
|
<li class="stats-list-negative">
|
20
20
|
<% days = data[:last_day_in_analysis] - data[:first_day_in_analysis] %>
|
21
|
-
<%= days > 0 ? "
|
21
|
+
<%= days > 0 ? "%d" % (data[:total_unique_visits] / days) : "N/A" %>
|
22
22
|
<span class="stats-list-label">Unique Visits / Day</span>
|
23
23
|
</li>
|
24
24
|
<li class="stats-list-negative">
|
@@ -5,7 +5,7 @@ table = Terminal::Table.new rows: [
|
|
5
5
|
["Days", data[:total_days_in_analysis]],
|
6
6
|
["Hits", data[:total_hits]],
|
7
7
|
["Unique Visits", data[:total_unique_visits]],
|
8
|
-
["Unique Visits / Day", data[:total_days_in_analysis] > 0 ? "
|
8
|
+
["Unique Visits / Day", data[:total_days_in_analysis] > 0 ? "%d" % (data[:total_unique_visits] / data[:total_days_in_analysis]) : "N/A"],
|
9
9
|
["Hits/Unique Visitor", data[:total_unique_visits] != 0 ? data[:total_hits] / data[:total_unique_visits] : "N/A"]
|
10
10
|
]
|
11
11
|
table.style = { border_i: "|" }
|
@@ -0,0 +1 @@
|
|
1
|
+
>>>> URLs IN THIS FILE ARE NOT SANITIZED AND MIGHT BE UNSAFE TO OPEN <<<<
|
@@ -1,7 +1,9 @@
|
|
1
1
|
<!doctype html>
|
2
2
|
<html class="no-js" lang="en">
|
3
3
|
<head>
|
4
|
-
<title
|
4
|
+
<title>
|
5
|
+
<%= options[:title] || "Log Sense: #{data[:filenames].empty? ? "stdin" : data[:filenames].join(", ")}" %>
|
6
|
+
</title>
|
5
7
|
|
6
8
|
<meta charset="utf-8" />
|
7
9
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
@@ -150,42 +152,11 @@
|
|
150
152
|
<body>
|
151
153
|
<div class="off-canvas-wrapper">
|
152
154
|
<div class="off-canvas position-left" id="offCanvas" data-off-canvas>
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
<% [ "Summary",
|
157
|
-
"Log Structure",
|
158
|
-
"Daily Distribution",
|
159
|
-
"Time Distribution",
|
160
|
-
"20_ and 30_ on HTML pages",
|
161
|
-
"20_ and 30_ on other resources",
|
162
|
-
"40_ and 50_ on HTML pages",
|
163
|
-
"40_ and 50_ on other Resources",
|
164
|
-
"Statuses",
|
165
|
-
"Daily Statuses",
|
166
|
-
"Browsers",
|
167
|
-
"Platforms",
|
168
|
-
"Referers",
|
169
|
-
"IPs",
|
170
|
-
"Geolocation",
|
171
|
-
"Streaks",
|
172
|
-
"Command Invocation",
|
173
|
-
"Performance"
|
174
|
-
].each do |item| %>
|
175
|
-
<li class="nav-item">
|
176
|
-
<a href="#<%= item.downcase.gsub(' ', '-') %>" data-close><%= item %></a>
|
177
|
-
</li>
|
178
|
-
<% end %>
|
179
|
-
</ul>
|
180
|
-
|
181
|
-
<p>
|
182
|
-
Generated by
|
183
|
-
<a href="https://github.com/avillafiorita/log_sense">LogSense</a> <br />
|
184
|
-
on <%= DateTime.now.strftime("%Y-%m-%d %H:%M") %>.<br />
|
185
|
-
<a href='https://db-ip.com'>IP Geolocation by DB-IP</a>
|
186
|
-
</p>
|
187
|
-
</nav>
|
155
|
+
<%= render "navigation.html.erb",
|
156
|
+
menus: Emitter::apache_report_specification.map { |x| x[:title] } +
|
157
|
+
["Streaks", "Command Invocation", "Performance"] %>
|
188
158
|
</div>
|
159
|
+
|
189
160
|
<div class="off-canvas-content grid-container grid-x fluid" data-off-canvas-content>
|
190
161
|
<div data-sticky-container>
|
191
162
|
<div class="sticky" data-sticky data-margin-top="0">
|
@@ -196,14 +167,14 @@
|
|
196
167
|
</div>
|
197
168
|
|
198
169
|
<section class="main-section">
|
199
|
-
<h1><%= options[:title] || "Log Sense
|
170
|
+
<h1><%= options[:title] || "Log Sense Apache Log Report" %></h1>
|
200
171
|
|
201
|
-
<p><b>Input File:</b> <%=
|
172
|
+
<p><b>Input File(s):</b> <%= data[:filenames].empty? ? "stdin" : data[:filenames].join(", ") %></p>
|
202
173
|
|
203
174
|
<div class="grid-x grid-margin-x">
|
204
175
|
<article class="card small-12 large-6 cell">
|
205
176
|
<div class="card-divider">
|
206
|
-
<h2 id="
|
177
|
+
<h2 id="<%= Emitter::slugify "Summary" %>">Summary</h2>
|
207
178
|
</div>
|
208
179
|
<div class="card-section">
|
209
180
|
<%= render "summary.html.erb", data: data %>
|
@@ -212,7 +183,7 @@
|
|
212
183
|
|
213
184
|
<article class="card cell small-12 large-6">
|
214
185
|
<div class="card-divider">
|
215
|
-
<h2 id="
|
186
|
+
<h2 id="<%= Emitter::slugify "Summary" %>">Log Structure</h2>
|
216
187
|
</div>
|
217
188
|
<div class="card-section">
|
218
189
|
<%= render "log_structure.html.erb", data: data %>
|
@@ -224,7 +195,7 @@
|
|
224
195
|
<% @reports.each_with_index do |report, index| %>
|
225
196
|
<article class="card cell <%= report[:col] || "small-12 large-6" %>" >
|
226
197
|
<div class="card-divider">
|
227
|
-
<h2 id="<%= report[:title]
|
198
|
+
<h2 id="<%= Emitter::slugify report[:title] %>">
|
228
199
|
<%= report[:title] %>
|
229
200
|
</h2>
|
230
201
|
</div>
|
@@ -252,101 +223,10 @@
|
|
252
223
|
<% end %>
|
253
224
|
</div>
|
254
225
|
|
255
|
-
<article class="card">
|
256
|
-
<div class="card-divider">
|
257
|
-
<h2 id="geolocation">Geolocation</h2>
|
258
|
-
</div>
|
259
|
-
<div class="card-section">
|
260
|
-
<table id="geolocation-table" class="table unstriped">
|
261
|
-
<thead>
|
262
|
-
<tr>
|
263
|
-
<th>Country Code</th>
|
264
|
-
<th>Total Hits</th>
|
265
|
-
<th>Total Visits</th>
|
266
|
-
<th>IPs</th>
|
267
|
-
</tr>
|
268
|
-
</thead>
|
269
|
-
<tbody>
|
270
|
-
<%# IP, Hits, Visits Size, Country%>
|
271
|
-
<% data[:ips].group_by { |x| x[4] }.each do |k, v| %>
|
272
|
-
<tr>
|
273
|
-
<td class="country"><%= k %></td>
|
274
|
-
<td class="total-hits"><%= v.map { |x| x[1] }.inject(&:+) %></td>
|
275
|
-
<td class="total-visits"><%= v.map { |x| x[2] }.inject(&:+) %></td>
|
276
|
-
<td class="ips">
|
277
|
-
<%= v.map { |x| "<a href=\"https://whatismyipaddress.com/ip/#{x[0]}\">#{x[0]}</a>" }.join(", ") %>
|
278
|
-
</td>
|
279
|
-
</tr>
|
280
|
-
<% end %>
|
281
|
-
</tbody>
|
282
|
-
</table>
|
283
|
-
</div>
|
284
|
-
</article>
|
285
|
-
|
286
|
-
<article class="card">
|
287
|
-
<div class="card-divider">
|
288
|
-
<h2 id="streaks">Streaks</h2>
|
289
|
-
</div>
|
290
|
-
<div class="card-section">
|
291
|
-
<table id="streaks-table" class="table data-table streaks">
|
292
|
-
<thead>
|
293
|
-
<tr>
|
294
|
-
<th>IP</th>
|
295
|
-
<th>
|
296
|
-
<div class="grid-x grid-margin-x">
|
297
|
-
<div class="small-2 cell">
|
298
|
-
Day
|
299
|
-
</div>
|
300
|
-
<div class="small-10 cell">
|
301
|
-
Resources
|
302
|
-
</div>
|
303
|
-
</div>
|
304
|
-
</th>
|
305
|
-
</tr>
|
306
|
-
</thead>
|
307
|
-
<tbody>
|
308
|
-
<% data[:streaks].group_by(&:first).each do |ip, date_urls| %>
|
309
|
-
<tr>
|
310
|
-
<td class="ip">
|
311
|
-
<a href="https://whatismyipaddress.com/ip/<%= ip %>"><%= ip %></a>
|
312
|
-
</td>
|
313
|
-
<td class="streaks">
|
314
|
-
<div class="grid-x grid-margin-x">
|
315
|
-
<% date_urls.group_by { |x| x[1] }.each do |date, urls| %>
|
316
|
-
<div class="small-12 medium-1 cell">
|
317
|
-
<span class="date"><%= date %></span>
|
318
|
-
</div>
|
319
|
-
<div class="small-12 medium-5 cell">
|
320
|
-
<span class="res-title">HTML:</span>
|
321
|
-
<% unique_with_count = urls.map { |x| x[2] }.compact.group_by{|e| e}.map{|k, v| [k, v.length]} %>
|
322
|
-
<ul class="no-bullet">
|
323
|
-
<% unique_with_count.select { |x| x[0].match /.*\.html?/ }.each do |url| %>
|
324
|
-
<li>[<%= url[1] %>] <%= Emitter::escape_javascript url[0] %></li>
|
325
|
-
<% end %>
|
326
|
-
</ul>
|
327
|
-
</div>
|
328
|
-
<div class=" small-12 medium-5 cell">
|
329
|
-
<span class="res-title">Other Resources:</span>
|
330
|
-
<ul class="no-bullet">
|
331
|
-
<% unique_with_count.select { |x| x[0] and ! x[0].match /.*\.html?/ }.each do |url| %>
|
332
|
-
<li>[<%= url[1] %>] <%= Emitter::escape_javascript url[0] %></li>
|
333
|
-
<% end %>
|
334
|
-
</ul>
|
335
|
-
</div>
|
336
|
-
<% end %>
|
337
|
-
</div>
|
338
|
-
</td>
|
339
|
-
</tr>
|
340
|
-
<% end %>
|
341
|
-
</tbody>
|
342
|
-
</table>
|
343
|
-
</div>
|
344
|
-
</article>
|
345
|
-
|
346
226
|
<div class="grid-x grid-margin-x">
|
347
227
|
<div class="cell small-12 large-6">
|
348
228
|
<article>
|
349
|
-
<h2 id="
|
229
|
+
<h2 id="<%= Emitter::slugify "Command Invocation" %>">Command Invocation</h2>
|
350
230
|
|
351
231
|
<%= render "command_invocation.html.erb", data: data, options: options %>
|
352
232
|
</article>
|
@@ -354,7 +234,7 @@
|
|
354
234
|
|
355
235
|
<div class="small-12 large-6 cell">
|
356
236
|
<article>
|
357
|
-
<h2 id="
|
237
|
+
<h2 id="<%= Emitter::slugify "Performance" %>">Performance</h2>
|
358
238
|
|
359
239
|
<%= render "performance.html.erb", data: data %>
|
360
240
|
</article>
|
@@ -1,30 +1,17 @@
|
|
1
1
|
* Apache Log Analysis
|
2
2
|
|
3
|
-
|
4
|
-
>>>> (USE THE HTML EXPORT FUNCTION INSTEAD) <<<<
|
3
|
+
<%= render "warning.txt.erb" %>
|
5
4
|
|
6
5
|
** Summary
|
7
6
|
|
8
7
|
<%= render "summary.txt.erb", data: data %>
|
9
8
|
|
10
|
-
<% @reports.each do |report| %>
|
9
|
+
<% @reports.reject { |x| x[:report] == :html }.each do |report| %>
|
11
10
|
** <%= report[:title] %>
|
12
11
|
|
13
12
|
<%= render "output_table.txt.erb", report: report, data: data %>
|
14
13
|
<% end %>
|
15
14
|
|
16
|
-
** Geolocation
|
17
|
-
|
18
|
-
<%=
|
19
|
-
ips = data[:ips].group_by { |x| x[4] }.map { |k, v|
|
20
|
-
[k, v.map { |x| x[1] }.inject(&:+), v.map { |x| x[2] }.inject(&:+) ]
|
21
|
-
}
|
22
|
-
table = Terminal::Table.new headings: ["Country", "Hits", "Total Visits"], rows: ips
|
23
|
-
table.style = { border_i: "|" }
|
24
|
-
(1..2).map { |i| table.align_column(i, :right) }
|
25
|
-
table
|
26
|
-
%>
|
27
|
-
|
28
15
|
** Command Invocation
|
29
16
|
|
30
17
|
<%= render 'command_invocation.txt.erb', data: data %>
|
@@ -1,7 +1,9 @@
|
|
1
1
|
<!doctype html>
|
2
2
|
<html class="no-js" lang="en">
|
3
3
|
<head>
|
4
|
-
<title
|
4
|
+
<title>
|
5
|
+
<%= options[:title] || "Log Sense: #{data[:filenames].empty? ? "stdin" : data[:filenames].join(", ")}" %>
|
6
|
+
</title>
|
5
7
|
|
6
8
|
<meta charset="utf-8" />
|
7
9
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
@@ -150,36 +152,8 @@
|
|
150
152
|
<body>
|
151
153
|
<div class="off-canvas-wrapper">
|
152
154
|
<div class="off-canvas position-left" id="offCanvas" data-off-canvas>
|
153
|
-
|
154
|
-
|
155
|
-
<ul class="no-bullet">
|
156
|
-
<% [
|
157
|
-
"Summary",
|
158
|
-
"Log Structure",
|
159
|
-
"Daily Distribution",
|
160
|
-
"Time Distribution",
|
161
|
-
"Statuses",
|
162
|
-
"Rails Performance",
|
163
|
-
"Fatal Events",
|
164
|
-
"Internal Server Errrors",
|
165
|
-
"Errors",
|
166
|
-
"IPs",
|
167
|
-
"Command Invocation",
|
168
|
-
"Performance"
|
169
|
-
].each do |item| %>
|
170
|
-
<li class="nav-item">
|
171
|
-
<a href="#<%= item.downcase.gsub(' ', '-') %>" data-close><%= item %></a>
|
172
|
-
</li>
|
173
|
-
<% end %>
|
174
|
-
</ul>
|
175
|
-
|
176
|
-
<p>
|
177
|
-
Generated by
|
178
|
-
<a href="https://github.com/avillafiorita/log_sense">LogSense</a> <br />
|
179
|
-
on <%= DateTime.now.strftime("%Y-%m-%d %H:%M") %>.<br />
|
180
|
-
<a href='https://db-ip.com'>IP Geolocation by DB-IP</a>
|
181
|
-
</p>
|
182
|
-
</nav>
|
155
|
+
<%= render "navigation.html.erb",
|
156
|
+
menus: Emitter::rails_report_specification.map { |x| x[:title] } %>
|
183
157
|
</div>
|
184
158
|
<div class="off-canvas-content grid-container grid-x fluid" data-off-canvas-content>
|
185
159
|
<div data-sticky-container>
|
@@ -191,14 +165,14 @@
|
|
191
165
|
</div>
|
192
166
|
|
193
167
|
<section class="main-section">
|
194
|
-
<h1><%= options[:title] || "Log Sense
|
168
|
+
<h1><%= options[:title] || "Log Sense Rails Log Report" %></h1>
|
195
169
|
|
196
|
-
<p><b>Input File:</b> <%=
|
170
|
+
<p><b>Input File(s):</b> <%= data[:filenames].empty? ? "stdin" : data[:filenames].join(", ") %></p>
|
197
171
|
|
198
172
|
<div class="grid-x grid-margin-x">
|
199
173
|
<article class="card small-12 large-6 cell">
|
200
174
|
<div class="card-divider">
|
201
|
-
<h2 id="
|
175
|
+
<h2 id="<%= Emitter::slugify "Summary" %>">Summary</h2>
|
202
176
|
</div>
|
203
177
|
<div class="card-section">
|
204
178
|
<%= render "summary.html.erb", data: data %>
|
@@ -207,7 +181,7 @@
|
|
207
181
|
|
208
182
|
<article class="card cell small-12 large-6">
|
209
183
|
<div class="card-divider">
|
210
|
-
<h2 id="
|
184
|
+
<h2 id="<%= Emitter::slugify "Log Structure" %>">Log Structure</h2>
|
211
185
|
</div>
|
212
186
|
<div class="card-section">
|
213
187
|
<%= render "log_structure.html.erb", data: data %>
|
@@ -249,7 +223,9 @@
|
|
249
223
|
<div class="grid-x grid-margin-x">
|
250
224
|
<div class="cell small-12 large-6">
|
251
225
|
<article>
|
252
|
-
<h2 id="
|
226
|
+
<h2 id="<%= Emitter::slugify "Command Invocation" %>">
|
227
|
+
Command Invocation
|
228
|
+
</h2>
|
253
229
|
|
254
230
|
<%= render "command_invocation.html.erb", data: data, options: options %>
|
255
231
|
</article>
|
@@ -257,7 +233,7 @@
|
|
257
233
|
|
258
234
|
<div class="small-12 large-6 cell">
|
259
235
|
<article>
|
260
|
-
<h2 id="
|
236
|
+
<h2 id="<%= Emitter::slugify "Performance" %>"> Performance</h2>
|
261
237
|
|
262
238
|
<%= render "performance.html.erb", data: data %>
|
263
239
|
</article>
|
@@ -1,13 +1,12 @@
|
|
1
1
|
* Rails Log Analysis
|
2
2
|
|
3
|
-
|
4
|
-
>>>> (USE THE HTML EXPORT FUNCTION INSTEAD) <<<<
|
3
|
+
<%= render "warning.txt.erb" %>
|
5
4
|
|
6
5
|
** Summary
|
7
6
|
|
8
7
|
<%= render "summary.txt.erb", data: data %>
|
9
8
|
|
10
|
-
<% @reports.each do |report| %>
|
9
|
+
<% @reports.reject { |x| x[:report] == :html }.each do |report| %>
|
11
10
|
** <%= report[:title] %>
|
12
11
|
|
13
12
|
<%= render "output_table.txt.erb", report: report, data: data %>
|
@@ -15,9 +14,9 @@
|
|
15
14
|
|
16
15
|
** Command Invocation
|
17
16
|
|
18
|
-
<%= render
|
17
|
+
<%= render 'command_invocation.txt.erb', data: data %>
|
19
18
|
|
20
|
-
**
|
19
|
+
** Performance
|
21
20
|
|
22
|
-
<%= render
|
21
|
+
<%= render 'performance.txt.erb', data: data %>
|
23
22
|
|
data/lib/log_sense/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: log_sense
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adolfo Fibrillation
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: browser
|
@@ -141,6 +141,7 @@ files:
|
|
141
141
|
- lib/log_sense/templates/_command_invocation.html.erb
|
142
142
|
- lib/log_sense/templates/_command_invocation.txt.erb
|
143
143
|
- lib/log_sense/templates/_log_structure.html.erb
|
144
|
+
- lib/log_sense/templates/_navigation.html.erb
|
144
145
|
- lib/log_sense/templates/_output_table.html.erb
|
145
146
|
- lib/log_sense/templates/_output_table.txt.erb
|
146
147
|
- lib/log_sense/templates/_performance.html.erb
|
@@ -148,6 +149,7 @@ files:
|
|
148
149
|
- lib/log_sense/templates/_report_data.html.erb
|
149
150
|
- lib/log_sense/templates/_summary.html.erb
|
150
151
|
- lib/log_sense/templates/_summary.txt.erb
|
152
|
+
- lib/log_sense/templates/_warning.txt.erb
|
151
153
|
- lib/log_sense/templates/apache.html.erb
|
152
154
|
- lib/log_sense/templates/apache.txt.erb
|
153
155
|
- lib/log_sense/templates/rails.html.erb
|