apache_log_report 1.1.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
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: []