apache_log_report 1.1.0 → 1.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f35ffbe99784dc77cbd61bb7b949237b9b380448b4829e442f0ad17513adc7b7
4
- data.tar.gz: 1d5030f64425293895b4b1fc84199178971bd34bde0d9b8450e868ec1d427cc3
3
+ metadata.gz: 8b90448ec269bd64d60c2323888fb77ea8ac7307a00198434119a6f0da605585
4
+ data.tar.gz: 24569c78964b795478c50e5de01c4f42fb2be8f02c30911e4e81229a96917a55
5
5
  SHA512:
6
- metadata.gz: 3eed1f80dec99429d5a3f25a99fd6e7ceca28547df852d97422a7cdb11b5d045592eb1c7e15443aca6c38744a7b62f59422049783f86d9b379039374c1b4ef09
7
- data.tar.gz: 7de8e31cc0024d34191b6b70f8716bc4d31e037764e0f776d75a806490fe1f8cb170c8c989c570fb84f0329c0f12298d5a3bc39b32caa3cdcd826775c3ceaaae
6
+ metadata.gz: 604697690824080fbbed523f26c1727f969fcf791803ebd15d6838384c5c77edbcde25ad74fa47c2371c0690019745948b6d78f9f1a51e6742e51e35c5c5ba76
7
+ data.tar.gz: b7d5a6940308a6a098cb61b9118cd17760592e299e091193b07a003ae87e6f7058ee1aab81aed2cadd65dcc07c6b64652ede126274be5be552dbbf0ca3ba11b1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- apache_log_report (1.1.0)
4
+ apache_log_report (1.1.2)
5
5
  apache_log-parser
6
6
  browser
7
7
  sqlite3
data/README.org CHANGED
@@ -14,10 +14,8 @@ See the [[file:CHANGELOG.org][CHANGELOG]] file.
14
14
 
15
15
  * Todo
16
16
 
17
- ** TODO Version information from command line and in reports
18
- ** TODO Refactor code from one giant class to more manageable chunkes
19
- ** TODO Move performance stats var to class (to isolate vars)
20
- ** TODO Check total number of days (which is not working, now)
17
+ ** TODO Graphs in HTML output
18
+ ** TODO Countries
21
19
 
22
20
  * Compatibility
23
21
 
@@ -6,8 +6,8 @@ Gem::Specification.new do |spec|
6
6
  spec.authors = ["Adolfo Villafiorita"]
7
7
  spec.email = ["adolfo.villafiorita@ict4g.net"]
8
8
 
9
- spec.summary = %q{Generate a request report in OrgMode format from an Apache log file.}
10
- spec.description = %q{Generate a request report in OrgMode format from an Apache log file.}
9
+ spec.summary = %q{Generate analytics from an Apache log file.}
10
+ spec.description = %q{Generate requests reports in HTML, OrgMode, and SQLite format from an Apache log file.}
11
11
  spec.homepage = "https://www.ict4g.net/gitea/adolfo/apache_log_report"
12
12
  spec.license = "MIT"
13
13
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
@@ -23,21 +23,28 @@ end
23
23
  @started_at = Time.now
24
24
 
25
25
  @db = ApacheLogReport::LogParser.parse @log_file
26
- @data = ApacheLogReport::DataCruncher.crunch @db, @options
27
26
 
28
- @ended_at = Time.now
29
- @duration = @ended_at - @started_at
30
-
31
- @data = @data.merge({
32
- command: @command,
33
- log_file: @log_file,
34
- started_at: @started_at,
35
- ended_at: @ended_at,
36
- duration: @duration
37
- })
38
-
39
- #
40
- # Emit Output
41
- #
42
-
43
- puts ApacheLogReport::Emitter.emit @data, @options
27
+ if @options[:output_format] == "sqlite"
28
+ ddb = SQLite3::Database.new('db.sqlite3')
29
+ b = SQLite3::Backup.new(ddb, 'main', @db, 'main')
30
+ b.step(-1) #=> DONE
31
+ b.finish
32
+ else
33
+ @data = ApacheLogReport::DataCruncher.crunch @db, @options
34
+
35
+ @ended_at = Time.now
36
+ @duration = @ended_at - @started_at
37
+
38
+ @data = @data.merge({
39
+ command: @command,
40
+ log_file: @log_file,
41
+ started_at: @started_at,
42
+ ended_at: @ended_at,
43
+ duration: @duration
44
+ })
45
+
46
+ #
47
+ # Emit Output
48
+ #
49
+ puts ApacheLogReport::Emitter.emit @data, @options
50
+ end
@@ -71,6 +71,8 @@ module ApacheLogReport
71
71
  @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]}"
72
72
  @referers = db.execute "SELECT referer, count(referer), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where #{filter} group by referer order by count(referer) desc limit #{options[:limit]}"
73
73
 
74
+ @streaks = db.execute "SELECT ip, substr(datetime, 1, 10), path from LogLine order by ip, datetime"
75
+
74
76
  data = {}
75
77
  self.instance_variables.each do |variable|
76
78
  var_as_symbol = variable.to_s[1..-1].to_sym
@@ -1,7 +1,6 @@
1
1
  require 'terminal-table'
2
2
  require 'erb'
3
3
  require 'ostruct'
4
- require 'byebug'
5
4
 
6
5
  module ApacheLogReport
7
6
  module Emitter
@@ -50,7 +50,7 @@ module ApacheLogReport
50
50
  args[:code_export] = n
51
51
  end
52
52
 
53
- opts.on("-fFORMAT", "--format=FORMAT", String, "Output format: html, org. Defaults to org mode") do |n|
53
+ opts.on("-fFORMAT", "--format=FORMAT", String, "Output format: html, org, sqlite. Defaults to org mode") do |n|
54
54
  args[:output_format] = n
55
55
  end
56
56
 
@@ -4,7 +4,7 @@ def slugify string
4
4
  end
5
5
  %>
6
6
 
7
- <table id="<%= slugify(title || "") %>" class="<%= slugify(title || "") %>">
7
+ <table id="<%= slugify(title || "") %>" class="table <%= slugify(title || "") %>">
8
8
  <thead>
9
9
  <tr>
10
10
  <% header.each do |heading| %>
@@ -4,34 +4,106 @@
4
4
  <meta name="author" content="apache_log_report">
5
5
 
6
6
  <link rel="stylesheet" href="alr-styles.css"></style>
7
+
8
+ <link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre.min.css">
9
+ <link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-exp.min.css">
10
+ <link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-icons.min.css">
7
11
  </head>
8
12
 
9
- <body>
10
- <section>
11
- <h1>Apache Log Analysis: <%= data[:log_file] || "stdin" %></h1>
13
+ <body class="container">
14
+ <h1>Apache Log Analysis: <%= data[:log_file] || "stdin" %></h1>
15
+
16
+ <nav>
17
+ <ul class="nav">
18
+ <li class="nav-item active">
19
+ <a href="#">Navigation</a>
20
+ <ul class="nav">
21
+ <% [ "Summary",
22
+ "Log Structure",
23
+ "Daily Distribution",
24
+ "Time Distribution",
25
+ "Most Requested Pages",
26
+ "Most Requested Resources",
27
+ "404 on HTML Files",
28
+ "404 on other Resources",
29
+ "Attacks",
30
+ "Statuses",
31
+ "Daily Statuses",
32
+ "Browsers",
33
+ "Platforms",
34
+ "Referers",
35
+ "IPs"
36
+ ].each do |item| %>
37
+ <li class="nav-item">
38
+ <a href="#<%= item.downcase.gsub(' ', '-') %>"><%= item %></a>
39
+ </li>
40
+ <% end %>
41
+ </ul>
42
+ </li>
43
+ </ul>
44
+ </nav>
12
45
 
13
- <article>
14
- <h2>Summary</h2>
15
-
16
- <table class="pure-table summary">
17
- <tr>
18
- <th class="hits">Hits</th>
19
- <td class="hits"><%= data[:total_hits][0][0] %></td>
20
- </tr>
21
- <tr>
22
- <th class="unique-visitors">Unique Visitors</th>
23
- <td class="unique-visitors"><%= data[:total_unique_visitors][0][0] %></td>
24
- </tr>
25
- <tr>
26
- <th class="tx">Tx</th>
27
- <td class="tx"><%= data[:total_size][0][0] %></td>
28
- </tr>
29
- <tr>
30
- <th class="days">Days </th>
31
- <td class="days"><%= data[:total_days][0][0] %></td>
32
- </tr>
33
- </table>
34
- </article>
46
+ <section>
47
+ <div class="columns">
48
+ <article class="col-6 column">
49
+ <h2 id="summary">Summary</h2>
50
+
51
+ <table class="table summary">
52
+ <tr>
53
+ <th class="hits">Hits</th>
54
+ <td class="hits"><%= data[:total_hits][0][0] %></td>
55
+ </tr>
56
+ <tr>
57
+ <th class="unique-visitors">Unique Visitors</th>
58
+ <td class="unique-visitors"><%= data[:total_unique_visitors][0][0] %></td>
59
+ </tr>
60
+ <tr>
61
+ <th class="tx">Tx</th>
62
+ <td class="tx"><%= data[:total_size][0][0] %></td>
63
+ </tr>
64
+ <tr>
65
+ <th class="period">Period</th>
66
+ <td class="period">
67
+ <%= data[:first_day][0][0] %>
68
+ --
69
+ <%= data[:last_day][0][0] %>
70
+ </td>
71
+ </tr>
72
+ <tr>
73
+ <th class="days">Days </th>
74
+ <td class="days"><%= data[:total_days] %></td>
75
+ </tr>
76
+ </table>
77
+ </article>
78
+ <article class="column col-6">
79
+ <h2 id="log-structure">Log Structure</h2>
80
+
81
+ <table class="table log-structure">
82
+ <tbody>
83
+ <tr>
84
+ <th>Input file</th>
85
+ <td><b><%= (data[:log_file] || "stdin") %></b></td>
86
+ </tr>
87
+ <tr>
88
+ <th>Log size</th>
89
+ <td><%= data[:log_size][0][0] %></td>
90
+ </tr>
91
+ <tr>
92
+ <th>Self poll entries</th>
93
+ <td><%= data[:selfpolls_size][0][0] %></td>
94
+ </tr>
95
+ <tr>
96
+ <th>Crawlers</th>
97
+ <td><%= data[:crawlers_size][0][0] %></td>
98
+ </tr>
99
+ <tr>
100
+ <th>Entries considered</th>
101
+ <td><%= data[:total_hits][0][0] %></td>
102
+ </tr>
103
+ </tbody>
104
+ </table>
105
+ </article>
106
+ </div>
35
107
 
36
108
  <% @reports = [
37
109
  { title: "Daily Distribution", header: ["Day", "Hits", "Visits", "Size"], rows: data[:daily_distribution] },
@@ -41,120 +113,137 @@
41
113
  { title: "404 on HTML Files", header: ["Path", "Hits", "Visitors"], rows: data[:missed_pages] },
42
114
  { title: "404 on other Resources", header: ["Path", "Hits", "Visitors"], rows: data[:missed_resources] },
43
115
  { title: "Attacks", header: ["Path", "Hits", "Visitors"], rows: data[:attacks] },
116
+ { },
44
117
  { title: "Statuses", header: ["Status", "Count"], rows: data[:statuses] },
45
118
  { title: "Daily Statuses", header: ["Status", "2xx", "3xx", "4xx"], rows: data[:statuses_by_day] },
46
119
  { title: "Browsers", header: ["Browser", "Hits", "Visitors", "Size"], rows: data[:browsers] },
47
120
  { title: "Platforms", header: ["Platform", "Hits", "Visitors", "Size"], rows: data[:platforms] },
121
+ { title: "Referers", header: ["Referers", "Hits", "Visitors", "Size"], rows: data[:referers], col: "col-12" },
48
122
  { title: "IPs", header: ["IPs", "Hits", "Visitors", "Size"], rows: data[:ips] },
49
- { title: "Referers", header: ["Referers", "Hits", "Visitors", "Size"], rows: data[:referers] },
123
+ { },
50
124
  ]
51
125
  %>
52
- <% @reports.each do |report| %>
53
- <article>
54
- <h2><%= report[:title] %></h2>
55
- <%= render "output_table", report %>
56
- </article>
57
- <% end %>
126
+ <div class="columns">
127
+ <% @reports.each do |report| %>
128
+ <div class="column <%= report[:col] || "col-6" %>">
129
+ <article>
130
+ <% if report[:title] != nil %>
131
+ <h2 id="<%= report[:title].downcase.gsub(/ +/, '-') %>">
132
+ <%= report[:title] %>
133
+ </h2>
134
+ <%= render "output_table", report %>
135
+ <% end %>
136
+ </article>
137
+ </div>
138
+ <% end %>
139
+ </div>
58
140
 
59
141
  <article>
60
- <h2>Command Invocation and Performance</h2>
61
-
62
- <h3>Command Invocation</h3>
63
-
64
- <code>
65
- </code>
66
-
67
- <table class="pure-table command-invocation">
68
- <tbody>
69
- <tr>
70
- <th>CLI Command</th>
71
- <td><pre><%= data[:command] %></pre></td>
72
- </tr>
73
- <tr>
74
- <th>Input file</th>
75
- <td><code><%= (data[:log_file] || "stdin") %></code></td>
76
- </tr>
77
- <tr>
78
- <th>Ignore crawlers</th>
79
- <td><code><%= options[:ignore_crawlers] %></code></td></tr>
80
- <tr>
81
- <th>Only crawlers</th>
82
- <td><code><%= options[:only_crawlers] %></code></td>
83
- </tr>
84
- <tr>
85
- <th>No selfpoll</th>
86
- <td><code><%= options[:no_selfpoll] %></code></td>
87
- </tr>
88
- <tr>
89
- <th>Filter by date</th>
90
- <td>
91
- <code><%= (options[:from_date] != nil or options[:to_date] != nil) %></code>
92
- </td>
93
- </tr>
94
- <tr>
95
- <th>Prefix</th>
96
- <td><code><%= @prefix %></code></td>
97
- </tr>
98
- <tr>
99
- <th>Suffix</th>
100
- <td><code><%= @suffix %></code></td>
101
- </tr>
102
- </tbody>
103
- </table>
104
-
105
- <h3> Log Structure</h3>
106
-
107
- <table class="pure-table log-structure">
108
- <tbody>
109
- <tr>
110
- <th>Log size</th>
111
- <td><%= data[:log_size][0][0] %></td>
112
- </tr>
113
- <tr>
114
- <th>Self poll entries</th>
115
- <td><%= data[:selfpolls_size][0][0] %></td>
116
- </tr>
117
- <tr>
118
- <th>Crawlers</th>
119
- <td><%= data[:crawlers_size][0][0] %></td>
120
- </tr>
121
- <tr>
122
- <th>Entries considered</th>
123
- <td><%= data[:total_hits][0][0] %></td>
124
- </tr>
125
- </tbody>
126
- </table>
127
-
128
- <h3> Performance</h3>
129
-
130
- <table class="pure-table performance">
131
- <tbody>
132
- <tr>
133
- <th>Analysis started at</th>
134
- <td><%= data[:started_at].to_s %></td>
135
- </tr>
136
- <tr>
137
- <th>Analysis ended at</th>
138
- <td><%= data[:ended_at].to_s %></td>
139
- </tr>
140
- <tr>
141
- <th>Duration (sec)</th>
142
- <td><%= "%.1f" % data[:duration] %></td>
143
- </tr>
144
- <tr>
145
- <th>Duration (min)</th>
146
- <td><%= "%d" % (data[:duration] / 60 ) %></td>
147
- </tr>
148
- <tr>
149
- <th>Log size</th>
150
- <td><%= data[:log_size][0][0] %></td>
151
- </tr>
152
- <tr>
153
- <th>Lines/sec</th>
154
- <td><%= "%.2f" % (data[:log_size][0][0] / data[:duration]) %></td></tr>
155
- </tbody>
156
- </table>
157
- </article>
142
+ <h2 id="streaks">Streaks</h2>
143
+
144
+ <table class="table streaks">
145
+ <thead>
146
+ <tr>
147
+ <th>IP</th>
148
+ <th>Day and URL</th>
149
+ </tr>
150
+ </thead>
151
+ <tbody>
152
+ <% data[:streaks].group_by(&:first).each do |ip, date_urls| %>
153
+ <tr>
154
+ <td class="ip"><%= ip %></td>
155
+ <td class="streaks">
156
+ <% date_urls.group_by(&:first).each do |date, urls| %>
157
+ <% urls.each do |url| %>
158
+ <b><%= url[1] %>:</b> <%= url[2] %> <br />
159
+ <% end %>
160
+ <% end %>
161
+ </td>
162
+ </tr>
163
+ <% end %>
164
+ </tbody>
165
+ </table>
166
+ </article>
167
+
168
+ <div class="columns">
169
+ <div class="column col-6">
170
+ <article>
171
+ <h2 id="command-invocation">Command Invocation</h2>
172
+
173
+ <table class="table command-invocation">
174
+ <tbody>
175
+ <tr>
176
+ <th>CLI Command</th>
177
+ <td><pre><%= data[:command] %></pre></td>
178
+ </tr>
179
+ <tr>
180
+ <th>Input file</th>
181
+ <td><code><%= (data[:log_file] || "stdin") %></code></td>
182
+ </tr>
183
+ <tr>
184
+ <th>Ignore crawlers</th>
185
+ <td><code><%= options[:ignore_crawlers] %></code></td></tr>
186
+ <tr>
187
+ <th>Only crawlers</th>
188
+ <td><code><%= options[:only_crawlers] %></code></td>
189
+ </tr>
190
+ <tr>
191
+ <th>No selfpoll</th>
192
+ <td><code><%= options[:no_selfpoll] %></code></td>
193
+ </tr>
194
+ <tr>
195
+ <th>Filter by date</th>
196
+ <td>
197
+ <code><%= (options[:from_date] != nil or options[:to_date] != nil) %></code>
198
+ </td>
199
+ </tr>
200
+ <tr>
201
+ <th>Prefix</th>
202
+ <td><code><%= @prefix %></code></td>
203
+ </tr>
204
+ <tr>
205
+ <th>Suffix</th>
206
+ <td><code><%= @suffix %></code></td>
207
+ </tr>
208
+ </tbody>
209
+ </table>
210
+ </article>
211
+ </div>
212
+
213
+ <div class="column col-6">
214
+ <article>
215
+ <h2 id="performance"> Performance</h2>
216
+
217
+ <table class="table performance">
218
+ <tbody>
219
+ <tr>
220
+ <th>Analysis started at</th>
221
+ <td><%= data[:started_at].to_s %></td>
222
+ </tr>
223
+ <tr>
224
+ <th>Analysis ended at</th>
225
+ <td><%= data[:ended_at].to_s %></td>
226
+ </tr>
227
+ <tr>
228
+ <th>Duration (sec)</th>
229
+ <td><%= "%.1f" % data[:duration] %></td>
230
+ </tr>
231
+ <tr>
232
+ <th>Duration (min)</th>
233
+ <td><%= "%d" % (data[:duration] / 60 ) %></td>
234
+ </tr>
235
+ <tr>
236
+ <th>Log size</th>
237
+ <td><%= data[:log_size][0][0] %></td>
238
+ </tr>
239
+ <tr>
240
+ <th>Lines/sec</th>
241
+ <td><%= "%.2f" % (data[:log_size][0][0] / data[:duration]) %></td></tr>
242
+ </tbody>
243
+ </table>
244
+ </article>
245
+ </div>
246
+ </div>
158
247
  </section>
159
248
  </body>
160
249
  </html>
@@ -1,3 +1,3 @@
1
1
  module ApacheLogReport
2
- VERSION = "1.1.0"
2
+ VERSION = "1.1.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apache_log_report
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adolfo Villafiorita
@@ -66,7 +66,8 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- description: Generate a request report in OrgMode format from an Apache log file.
69
+ description: Generate requests reports in HTML, OrgMode, and SQLite format from an
70
+ Apache log file.
70
71
  email:
71
72
  - adolfo.villafiorita@ict4g.net
72
73
  executables:
@@ -121,5 +122,5 @@ requirements: []
121
122
  rubygems_version: 3.2.22
122
123
  signing_key:
123
124
  specification_version: 4
124
- summary: Generate a request report in OrgMode format from an Apache log file.
125
+ summary: Generate analytics from an Apache log file.
125
126
  test_files: []