log_sense 1.6.1 → 1.7.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 +12 -0
- data/Gemfile.lock +20 -14
- data/Rakefile +9 -10
- data/exe/log_sense +36 -19
- data/ip_locations/dbip-country-lite.sqlite3 +0 -0
- data/lib/log_sense/apache/log_line_parser.rb +59 -0
- data/lib/log_sense/apache/log_parser.rb +101 -0
- data/lib/log_sense/emitter.rb +29 -23
- data/lib/log_sense/ip_locator.rb +8 -5
- data/lib/log_sense/options/checker.rb +26 -0
- data/lib/log_sense/options/parser.rb +170 -0
- data/lib/log_sense/rails/log_parser.rb +409 -0
- data/lib/log_sense/rails_report_shaper.rb +1 -1
- data/lib/log_sense/templates/_cdn_links.html.erb +0 -4
- data/lib/log_sense/templates/_log_structure.html.erb +2 -2
- data/lib/log_sense/templates/_rails.css.erb +3 -2
- data/lib/log_sense/templates/_report_data.html.erb +1 -1
- data/lib/log_sense/templates/_stylesheet.css +21 -19
- data/lib/log_sense/templates/_summary.html.erb +2 -2
- data/lib/log_sense/templates/report_html.erb +1 -0
- data/lib/log_sense/version.rb +1 -1
- data/lib/log_sense.rb +4 -4
- metadata +8 -8
- data/lib/log_sense/apache_log_line_parser.rb +0 -57
- data/lib/log_sense/apache_log_parser.rb +0 -100
- data/lib/log_sense/options_checker.rb +0 -24
- data/lib/log_sense/options_parser.rb +0 -147
- data/lib/log_sense/rails_log_parser.rb +0 -313
@@ -1,10 +1,10 @@
|
|
1
1
|
<ul class="stats-list">
|
2
2
|
<li>
|
3
|
-
<%= data[:first_day_in_analysis]
|
3
|
+
<%= data[:first_day_in_analysis]&.strftime("%b %d, %Y") %>
|
4
4
|
<span class="stats-list-label">From</span>
|
5
5
|
</li>
|
6
6
|
<li>
|
7
|
-
<%= data[:last_day_in_analysis]
|
7
|
+
<%= data[:last_day_in_analysis]&.strftime("%b %d, %Y") %>
|
8
8
|
<span class="stats-list-label">To</span>
|
9
9
|
</li>
|
10
10
|
<li class="stats-list-positive">
|
data/lib/log_sense/version.rb
CHANGED
data/lib/log_sense.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require "log_sense/version"
|
2
2
|
|
3
|
-
require "log_sense/
|
4
|
-
require "log_sense/
|
3
|
+
require "log_sense/options/parser"
|
4
|
+
require "log_sense/options/checker"
|
5
5
|
|
6
|
-
require "log_sense/
|
7
|
-
require "log_sense/
|
6
|
+
require "log_sense/apache/log_parser"
|
7
|
+
require "log_sense/rails/log_parser"
|
8
8
|
|
9
9
|
require "log_sense/aggregator"
|
10
10
|
require "log_sense/apache_aggregator"
|
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adolfo Villafiorita
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: browser
|
@@ -130,16 +130,16 @@ files:
|
|
130
130
|
- ip_locations/dbip-country-lite.sqlite3
|
131
131
|
- lib/log_sense.rb
|
132
132
|
- lib/log_sense/aggregator.rb
|
133
|
+
- lib/log_sense/apache/log_line_parser.rb
|
134
|
+
- lib/log_sense/apache/log_parser.rb
|
133
135
|
- lib/log_sense/apache_aggregator.rb
|
134
|
-
- lib/log_sense/apache_log_line_parser.rb
|
135
|
-
- lib/log_sense/apache_log_parser.rb
|
136
136
|
- lib/log_sense/apache_report_shaper.rb
|
137
137
|
- lib/log_sense/emitter.rb
|
138
138
|
- lib/log_sense/ip_locator.rb
|
139
|
-
- lib/log_sense/
|
140
|
-
- lib/log_sense/
|
139
|
+
- lib/log_sense/options/checker.rb
|
140
|
+
- lib/log_sense/options/parser.rb
|
141
|
+
- lib/log_sense/rails/log_parser.rb
|
141
142
|
- lib/log_sense/rails_aggregator.rb
|
142
|
-
- lib/log_sense/rails_log_parser.rb
|
143
143
|
- lib/log_sense/rails_report_shaper.rb
|
144
144
|
- lib/log_sense/report_shaper.rb
|
145
145
|
- lib/log_sense/templates/_cdn_links.html.erb
|
@@ -188,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
188
|
- !ruby/object:Gem::Version
|
189
189
|
version: '0'
|
190
190
|
requirements: []
|
191
|
-
rubygems_version: 3.
|
191
|
+
rubygems_version: 3.3.26
|
192
192
|
signing_key:
|
193
193
|
specification_version: 4
|
194
194
|
summary: Generate analytics for Apache and Rails log file.
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module LogSense
|
2
|
-
# parses a log line and returns a hash
|
3
|
-
# LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
|
4
|
-
#
|
5
|
-
# %h: IP
|
6
|
-
# %l: ident or -
|
7
|
-
# %u: userid or -
|
8
|
-
# %t: [10/Oct/2000:13:55:36 -0700]
|
9
|
-
# day = 2*digit
|
10
|
-
# month = 3*letter
|
11
|
-
# year = 4*digit
|
12
|
-
# hour = 2*digit
|
13
|
-
# minute = 2*digit
|
14
|
-
# second = 2*digit
|
15
|
-
# zone = (`+' | `-') 4*digit
|
16
|
-
# %r: GET /apache_pb.gif HTTP/1.0
|
17
|
-
# %{User-agent}: "
|
18
|
-
#
|
19
|
-
# Example
|
20
|
-
# 116.179.32.16 - - [19/Dec/2021:22:35:11 +0100] "GET / HTTP/1.1" 200 135 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
|
21
|
-
#
|
22
|
-
class ApacheLogLineParser
|
23
|
-
DAY = /[0-9]{2}/
|
24
|
-
MONTH = /[A-Za-z]{3}/
|
25
|
-
YEAR = /[0-9]{4}/
|
26
|
-
TIMEC = /[0-9]{2}/
|
27
|
-
TIMEZONE = /(\+|-)[0-9]{4}/
|
28
|
-
|
29
|
-
IP = /(?<ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|::1)/
|
30
|
-
IDENT = /(?<ident>[^ ]+|-)/
|
31
|
-
USERID = /(?<userid>[^ ]+|-)/
|
32
|
-
|
33
|
-
TIMESTAMP = /(?<date>#{DAY}\/#{MONTH}\/#{YEAR}):(?<time>#{TIMEC}:#{TIMEC}:#{TIMEC} #{TIMEZONE})/
|
34
|
-
|
35
|
-
HTTP_METHODS = /GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH/
|
36
|
-
WEBDAV_METHODS = /COPY|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|UNLOCK/
|
37
|
-
OTHER_METHODS = /SEARCH|REPORT|PRI|HEAD\/robots.txt/
|
38
|
-
METHOD = /(?<method>#{HTTP_METHODS}|#{WEBDAV_METHODS}|#{OTHER_METHODS})/
|
39
|
-
PROTOCOL = /(?<protocol>HTTP\/[0-9]\.[0-9]|-|.*)/
|
40
|
-
URL = /(?<url>[^ ]+)/
|
41
|
-
REFERER = /(?<referer>[^"]*)/
|
42
|
-
RETURN_CODE = /(?<status>[1-5][0-9][0-9])/
|
43
|
-
SIZE = /(?<size>[0-9]+|-)/
|
44
|
-
USER_AGENT = /(?<user_agent>[^"]*)/
|
45
|
-
|
46
|
-
attr_reader :format
|
47
|
-
|
48
|
-
def initialize
|
49
|
-
@format = /#{IP} #{IDENT} #{USERID} \[#{TIMESTAMP}\] "(#{METHOD} #{URL} #{PROTOCOL}|-|.+)" #{RETURN_CODE} #{SIZE} "#{REFERER}" "#{USER_AGENT}"/
|
50
|
-
end
|
51
|
-
|
52
|
-
def parse(line)
|
53
|
-
@format.match(line) ||
|
54
|
-
raise("Apache LogLine Parser Error: Could not parse #{line}")
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,100 +0,0 @@
|
|
1
|
-
require "sqlite3"
|
2
|
-
require "browser"
|
3
|
-
require "log_sense/apache_log_line_parser"
|
4
|
-
|
5
|
-
module LogSense
|
6
|
-
#
|
7
|
-
# parse an Apache log file and return a SQLite3 DB
|
8
|
-
#
|
9
|
-
class ApacheLogParser
|
10
|
-
def parse(streams, options = {})
|
11
|
-
db = SQLite3::Database.new ":memory:"
|
12
|
-
|
13
|
-
db.execute "CREATE TABLE IF NOT EXISTS LogLine(
|
14
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
15
|
-
datetime TEXT,
|
16
|
-
ip TEXT,
|
17
|
-
user TEXT,
|
18
|
-
unique_visitor TEXT,
|
19
|
-
method TEXT,
|
20
|
-
path TEXT,
|
21
|
-
extension TEXT,
|
22
|
-
status TEXT,
|
23
|
-
size INTEGER,
|
24
|
-
referer TEXT,
|
25
|
-
user_agent TEXT,
|
26
|
-
bot INTEGER,
|
27
|
-
browser TEXT,
|
28
|
-
browser_version TEXT,
|
29
|
-
platform TEXT,
|
30
|
-
platform_version TEXT,
|
31
|
-
source_file TEXT,
|
32
|
-
line_number INTEGER
|
33
|
-
)"
|
34
|
-
|
35
|
-
ins = db.prepare("insert into LogLine (
|
36
|
-
datetime,
|
37
|
-
ip,
|
38
|
-
user,
|
39
|
-
unique_visitor,
|
40
|
-
method,
|
41
|
-
path,
|
42
|
-
extension,
|
43
|
-
status,
|
44
|
-
size,
|
45
|
-
referer,
|
46
|
-
user_agent,
|
47
|
-
bot,
|
48
|
-
browser,
|
49
|
-
browser_version,
|
50
|
-
platform,
|
51
|
-
platform_version,
|
52
|
-
source_file,
|
53
|
-
line_number
|
54
|
-
)
|
55
|
-
values (#{Array.new(18, '?').join(', ')})")
|
56
|
-
|
57
|
-
parser = ApacheLogLineParser.new
|
58
|
-
|
59
|
-
streams.each do |stream|
|
60
|
-
stream.readlines.each_with_index do |line, line_number|
|
61
|
-
begin
|
62
|
-
hash = parser.parse line
|
63
|
-
ua = Browser.new(hash[:user_agent], accept_language: 'en-us')
|
64
|
-
ins.execute(
|
65
|
-
DateTime.parse("#{hash[:date]}T#{hash[:time]}").iso8601,
|
66
|
-
hash[:ip],
|
67
|
-
hash[:userid],
|
68
|
-
unique_visitor_id(hash),
|
69
|
-
hash[:method],
|
70
|
-
hash[:url],
|
71
|
-
(hash[:url] ? File.extname(hash[:url]) : ''),
|
72
|
-
hash[:status],
|
73
|
-
hash[:size].to_i,
|
74
|
-
hash[:referer],
|
75
|
-
hash[:user_agent],
|
76
|
-
ua.bot? ? 1 : 0,
|
77
|
-
(ua.name || ''),
|
78
|
-
(ua.version || ''),
|
79
|
-
(ua.platform.name || ''),
|
80
|
-
(ua.platform.version || ''),
|
81
|
-
stream == $stdin ? "stdin" : stream.path,
|
82
|
-
line_number
|
83
|
-
)
|
84
|
-
rescue StandardError => e
|
85
|
-
warn e.message
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
db
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
def unique_visitor_id hash
|
96
|
-
"#{hash[:date]} #{hash[:ip]} #{hash[:user_agent]}"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module LogSense
|
2
|
-
#
|
3
|
-
# Check options and return appropriate error if
|
4
|
-
# command arguments are wrong
|
5
|
-
#
|
6
|
-
module OptionsChecker
|
7
|
-
SUPPORTED_CHAINS = {
|
8
|
-
rails: %i[txt html sqlite3 ufw],
|
9
|
-
apache: %i[txt html sqlite3 ufw]
|
10
|
-
}.freeze
|
11
|
-
|
12
|
-
def self.compatible?(iformat, oformat)
|
13
|
-
(SUPPORTED_CHAINS[iformat.to_sym] || []).include? oformat.to_sym
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.chains_to_s
|
17
|
-
string = ""
|
18
|
-
SUPPORTED_CHAINS.each do |iformat, oformat|
|
19
|
-
string << "- #{iformat}: #{oformat.join(", ")}\n"
|
20
|
-
end
|
21
|
-
string
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,147 +0,0 @@
|
|
1
|
-
require "optparse"
|
2
|
-
require "optparse/date"
|
3
|
-
require "log_sense/version"
|
4
|
-
require "log_sense/options_checker"
|
5
|
-
|
6
|
-
module LogSense
|
7
|
-
module OptionsParser
|
8
|
-
#
|
9
|
-
# parse command line options
|
10
|
-
#
|
11
|
-
def self.parse(options)
|
12
|
-
limit = 100
|
13
|
-
args = {}
|
14
|
-
|
15
|
-
opt_parser = OptionParser.new do |opts|
|
16
|
-
opts.banner = "Usage: log_sense [options] [logfile ...]"
|
17
|
-
|
18
|
-
opts.on("-tTITLE", "--title=TITLE",
|
19
|
-
String,
|
20
|
-
"Title to use in the report") do |optval|
|
21
|
-
args[:title] = optval
|
22
|
-
end
|
23
|
-
|
24
|
-
opts.on("-fFORMAT", "--input-format=FORMAT",
|
25
|
-
String,
|
26
|
-
"Input format (either rails or apache)") do |optval|
|
27
|
-
args[:input_format] = optval
|
28
|
-
end
|
29
|
-
|
30
|
-
opts.on("-iFORMAT", "--input-files=file,file,",
|
31
|
-
Array,
|
32
|
-
"Input files (can also be passed directly)") do |optval|
|
33
|
-
args[:input_filenames] = optval
|
34
|
-
end
|
35
|
-
|
36
|
-
opts.on("-tFORMAT", "--output-format=FORMAT",
|
37
|
-
String,
|
38
|
-
"Output format: html, org, txt, sqlite.") do |optval|
|
39
|
-
args[:output_format] = optval
|
40
|
-
end
|
41
|
-
|
42
|
-
opts.on("-oOUTPUT_FILE", "--output-file=OUTPUT_FILE",
|
43
|
-
String,
|
44
|
-
"Output file") do |n|
|
45
|
-
args[:output_filename] = n
|
46
|
-
end
|
47
|
-
|
48
|
-
opts.on("-bDATE", "--begin=DATE",
|
49
|
-
Date,
|
50
|
-
"Consider entries after or on DATE") do |optval|
|
51
|
-
args[:from_date] = optval
|
52
|
-
end
|
53
|
-
|
54
|
-
opts.on("-eDATE", "--end=DATE",
|
55
|
-
Date,
|
56
|
-
"Consider entries before or on DATE") do |optval|
|
57
|
-
args[:to_date] = optval
|
58
|
-
end
|
59
|
-
|
60
|
-
opts.on("-lN", "--limit=N",
|
61
|
-
Integer,
|
62
|
-
"Limit to the N most requested resources (defaults to #{limit})") do |optval|
|
63
|
-
args[:limit] = optval
|
64
|
-
end
|
65
|
-
|
66
|
-
opts.on("-wWIDTH", "--width=WIDTH",
|
67
|
-
Integer,
|
68
|
-
"Maximum width of long columns in textual reports") do |optval|
|
69
|
-
args[:width] = optval
|
70
|
-
end
|
71
|
-
|
72
|
-
opts.on("-rROWS", "--rows=ROWS",
|
73
|
-
Integer,
|
74
|
-
"Maximum number of rows for columns with multiple entries in textual reports") do |optval|
|
75
|
-
args[:inner_rows] = optval
|
76
|
-
end
|
77
|
-
|
78
|
-
opts.on("-pPATTERN", "--pattern=PATTERN",
|
79
|
-
String,
|
80
|
-
"Pattern to use with ufw report to decide IP to blacklist") do |optval|
|
81
|
-
args[:pattern] = optval
|
82
|
-
end
|
83
|
-
|
84
|
-
opts.on("-cPOLICY", "--crawlers=POLICY",
|
85
|
-
String,
|
86
|
-
"Decide what to do with crawlers (applies to Apache Logs)") do |optval|
|
87
|
-
case optval
|
88
|
-
when "only"
|
89
|
-
args[:only_crawlers] = true
|
90
|
-
when "ignore"
|
91
|
-
args[:ignore_crawlers] = true
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
opts.on("-ns", "--no-selfpoll",
|
96
|
-
"Ignore self poll entries (requests from ::1; applies to Apache Logs)") do
|
97
|
-
args[:no_selfpoll] = true
|
98
|
-
end
|
99
|
-
|
100
|
-
opts.on("-ng", "--no-geo",
|
101
|
-
"Do not geolocate entries") do
|
102
|
-
args[:geolocation] = false
|
103
|
-
end
|
104
|
-
|
105
|
-
opts.on("--verbose", "Inform about progress (output to STDERR)") do
|
106
|
-
args[:verbose] = true
|
107
|
-
end
|
108
|
-
|
109
|
-
opts.on("-v", "--version", "Prints version information") do
|
110
|
-
puts "log_sense version #{LogSense::VERSION}"
|
111
|
-
puts "Copyright (C) 2021 Shair.Tech"
|
112
|
-
puts "Distributed under the terms of the MIT license"
|
113
|
-
exit
|
114
|
-
end
|
115
|
-
|
116
|
-
opts.on("-h", "--help", "Prints this help") do
|
117
|
-
puts opts
|
118
|
-
puts
|
119
|
-
puts "This is version #{LogSense::VERSION}"
|
120
|
-
|
121
|
-
puts
|
122
|
-
puts "Output formats:"
|
123
|
-
puts
|
124
|
-
|
125
|
-
puts OptionsChecker.chains_to_s
|
126
|
-
|
127
|
-
exit 0
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
opt_parser.parse!(options)
|
132
|
-
|
133
|
-
args[:limit] ||= limit
|
134
|
-
args[:input_filenames] ||= []
|
135
|
-
args[:input_format] ||= "apache"
|
136
|
-
args[:output_format] ||= "html"
|
137
|
-
args[:ignore_crawlers] ||= false
|
138
|
-
args[:only_crawlers] ||= false
|
139
|
-
args[:no_selfpoll] ||= false
|
140
|
-
args[:verbose] ||= false
|
141
|
-
# if set to false leave, otherwise set to true
|
142
|
-
args[:geolocation] = true unless args[:geolocation] == false
|
143
|
-
|
144
|
-
args
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|