log_sense 1.2.2 → 1.3.2

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: 72d2fbdba26d6fa20cef0fc82c16ecd29eeb135ece6178804a9737d6bc95e838
4
- data.tar.gz: 5ac8a548f0f2ac3db060ac4375d4756539a5262b95b9aebc0fc496e6e0003caf
3
+ metadata.gz: 6498507bb6ae7e6ba5505609421154358790f095bd9cfe5ab912920b2409f7d1
4
+ data.tar.gz: 0ccb5fa005b8f1e47545bf98ce05273f07bd452d3be54c5bd1a7f124c96558d9
5
5
  SHA512:
6
- metadata.gz: 0bdecd45808f1257cb7aeaa97d2cf13cf0e62fbde74365c351a2e5440784e99bbfa29271746db66a5dba2bb5e1ae5891aaac4cb982f37ef8072d9e079e3b032a
7
- data.tar.gz: d88cca775be4a8828ebaecec757cee87e52338f36784c3a59d6ab8b703328982ecff71233640560f832c4b1f9d7fa4c299a47344d3b79139f518404e02f32380
6
+ metadata.gz: e12760ccdfc518c7221afa337f4ac1c8a77219cd3ed65510486e3ce945e6129519aa3fe93b05845a5430b9bc52359d70591e0fcbd346e971527af1760c304e5c
7
+ data.tar.gz: 8d38d8ed84287722b77f51599598b84a2257f9f86d2dd3815faf25dfb7db4ab16e68dbf737c7d005519ac5f9fe1d4b41cd896dfaf862eabb6a7dc4f033d2bb13
data/CHANGELOG.org CHANGED
@@ -2,13 +2,20 @@
2
2
  #+AUTHOR: Adolfo Villafiorita
3
3
  #+STARTUP: showall
4
4
 
5
- * Changes in log_sense 1.1.2
6
- <2021-12-17 Fri>
5
+ * 1.3.2
7
6
 
8
- - Added Rails Log HTML output
7
+ - [Code] HTML reports now generate JSON data which is shared between
8
+ DataTable and Vega Light: this should reduce page size and loading
9
+ time of HTML reports
10
+ - [Doc] Added screenshot and fixed some text
11
+ - [Doc] Fixes requirements on Ruby version
9
12
 
10
- * Changes in log_sense 1.1.1 and earlier
11
- <2021-12-17 Fri>
13
+ * 1.3.1
12
14
 
13
- - In the Git commit messages (not very informative, I am afraid).
15
+ - [Code] Strengthened parsing of Apache Logs (added WebDav and other methods)
14
16
 
17
+ * 1.3.0
18
+
19
+ - [Code] Removed dependency from =apache_log-parser= and implemented our own
20
+ parser for the combined format.
21
+
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- log_sense (1.0.7)
5
- apache_log-parser
4
+ log_sense (1.3.1)
6
5
  browser
7
6
  ipaddr
8
7
  iso_country_codes
@@ -12,7 +11,6 @@ PATH
12
11
  GEM
13
12
  remote: https://rubygems.org/
14
13
  specs:
15
- apache_log-parser (3.1.2)
16
14
  browser (5.3.1)
17
15
  byebug (11.1.3)
18
16
  ipaddr (1.2.3)
data/README.org CHANGED
@@ -4,24 +4,10 @@
4
4
 
5
5
  * Introduction
6
6
 
7
- LogSense generates reports and statistics from Apache web logs in the
8
- =combined= format and from Rails logs. Written in Ruby, it runs from
9
- the command line, it is fast, and it can be installed on any system
10
- which supports Ruby.
11
-
12
- LogSense moves along the lines of tools such as [[https://goaccess.io/][GoAccess]]
13
- and [[https://umami.is/][Umami]], focusing on privacy and data-ownership: the data
14
- generated by LogSense is stored on your computer and owned by
15
- you (like it should be).
16
-
17
- LogSense is also inspired by *static websites generators*: statistics
18
- are generated from the command line and accessed as static HTML files.
19
- By generating static resources, LogSense significantly reduces the
20
- attack surface of your webserver and installation headaches.
21
-
22
- We have, for instance, a cron job running on our servers, generating
23
- statistics at night. The generated files are then made available on a
24
- private area on the web.
7
+ LogSense generates reports and statistics from Apache and Ruby on
8
+ Rails logs. Written in Ruby, it runs from the command line, it is
9
+ fast, and it can be installed on any system with a relatively recent
10
+ version of Ruby. We tested on Ruby 2.6.9, Ruby 3.0.x and later.
25
11
 
26
12
  LogSense reports the following data:
27
13
 
@@ -40,7 +26,27 @@ LogSense reports the following data:
40
26
  Filters from the command line allow to analyze specific periods and
41
27
  distinguish traffic generated by self polls and crawlers.
42
28
 
43
- LogSense generates HTML, txt (Org Mode), and SQLite outputs.
29
+ LogSense generates HTML, txt, and SQLite outputs.
30
+
31
+ And, of course, the compulsory screenshot:
32
+
33
+ #+ATTR_HTML: :width 80%
34
+ [[file:./apache-screenshot.png]]
35
+
36
+ * Motivation
37
+
38
+ LogSense moves along the lines of tools such as [[https://goaccess.io/][GoAccess]] (which
39
+ strongly inspired the development of Log Sense) and [[https://umami.is/][Umami]], focusing on
40
+ *privacy* and *data-ownership*: the data generated by LogSense is
41
+ stored on your computer and owned by you (like it should be)[fn:1].
42
+
43
+ LogSense is also inspired by *static websites generators*: statistics
44
+ are generated from the command line and accessed as static HTML files.
45
+ LogSense thus significantly reduces the attack surface of your
46
+ webserver and installation headaches. We have, for instance, a Cron
47
+ job running on our servers, generating statistics at night. The
48
+ generated files are then made available on a private area on the web.
49
+
44
50
 
45
51
  * Installation
46
52
 
@@ -81,37 +87,48 @@ LogSense generates HTML, txt (Org Mode), and SQLite outputs.
81
87
  - txt
82
88
  #+end_example
83
89
 
90
+ Examples:
91
+
92
+ #+begin_example sh
93
+ log_sense -f apache -i access.log -t txt > access-data.txt
94
+ log_sense -f rails -i production.log -t html > performance.txt
95
+ #+end_example
96
+
84
97
  * Change Log
85
98
 
86
99
  See the [[file:CHANGELOG.org][CHANGELOG]] file.
87
100
 
88
101
  * Compatibility
89
102
 
90
- LogSense should run on any system on which Ruby runs.
103
+ LogSense should run on any system on which a recent version of Ruby
104
+ runs. We tested it with Ruby 2.6.9 and Ruby 3.x.x.
91
105
 
92
106
  Concerning the outputs:
93
107
 
94
- - The HTML report uses [[https://picturepan2.github.io/spectre/][Spectre.css]] and (will use) [[https://vega.github.io/vega-lite/][Vega Light]], which
95
- are downloaded from a CDN
96
- - The textual format is compatible with Org Mode and can be further
97
- processed to any format Org Mode can be exported to (including HTML
98
- and PDF),
108
+ - HTML reports use [[https://get.foundation/][Zurb Foundation]], [[https://www.datatables.net/][Data Tables]], and [[https://vega.github.io/vega-lite/][Vega Light]], which
109
+ are all downloaded from a CDN
110
+ - The textual format is compatible with [[https://orgmode.org/][Org Mode]] and can be further
111
+ processed to any format [[https://orgmode.org/][Org Mode]] can be exported to (including HTML
112
+ and PDF)
99
113
 
100
114
  * Author and Contributors
101
115
 
102
- [[http://ict4g.net/adolfo][Adolfo Villafiorita]].
116
+ [[https://shair.tech][Shair.Tech]]
103
117
 
104
118
  * Known Bugs
105
119
 
106
- Some known bugs and an unknown number of unknown bugs.
107
-
120
+ No known bugs; an unknown number of unknown bugs.
108
121
  (See the open issues for the known bugs.)
109
122
 
110
123
  * License
111
124
 
112
125
  Distributed under the terms of the [[http://opensource.org/licenses/MIT][MIT License]].
113
126
 
114
- Geolocation is made possible by the DB-IP.com IP to City database, released under
115
- a CC license.
116
-
127
+ Geolocation is made possible by the DB-IP.com IP to City database,
128
+ released under a CC license.
117
129
 
130
+ [fn:1] There is a small catch: CSS and JavaScript for layout and plots
131
+ are downloaded from a CDN. Technically, thus, if you generate HTML
132
+ reports and open them, a request is performed and the CDN might keep a
133
+ track (see [[https://en.wikipedia.org/wiki/Content_delivery_network#Security_and_privacy][CDN Security and Privacy on Wikipedia]] for more details).
134
+ Textual reports don't have this issue.
Binary file
@@ -0,0 +1,56 @@
1
+ module LogSense
2
+ class ApacheLogLineParser
3
+ # parses a query and makes it into an expression which can be evaluated
4
+ # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
5
+ #
6
+ # %h: IP
7
+ # %l: ident or -
8
+ # %u: userid or -
9
+ # %t: [10/Oct/2000:13:55:36 -0700]
10
+ # day = 2*digit
11
+ # month = 3*letter
12
+ # year = 4*digit
13
+ # hour = 2*digit
14
+ # minute = 2*digit
15
+ # second = 2*digit
16
+ # zone = (`+' | `-') 4*digit
17
+ # %r: GET /apache_pb.gif HTTP/1.0
18
+ # %{User-agent}: "
19
+ #
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
+ DAY = /[0-9]{2}/
23
+ MONTH = /[A-Za-z]{3}/
24
+ YEAR = /[0-9]{4}/
25
+ TIMEC = /[0-9]{2}/
26
+ TIMEZONE = /(\+|-)[0-9]{4}/
27
+
28
+ IP = /(?<ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|::1)/
29
+ IDENT = /(?<ident>[^ ]+|-)/
30
+ USERID = /(?<userid>[^ ]+|-)/
31
+
32
+ TIMESTAMP = /(?<date>#{DAY}\/#{MONTH}\/#{YEAR}):(?<time>#{TIMEC}:#{TIMEC}:#{TIMEC} #{TIMEZONE})/
33
+
34
+ HTTP_METHODS=/GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH/
35
+ WEBDAV_METHODS=/COPY|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|UNLOCK/
36
+ OTHER_METHODS=/SEARCH|REPORT/
37
+ METHOD=/(?<method>#{HTTP_METHODS}|#{WEBDAV_METHODS}|#{OTHER_METHODS})/
38
+ PROTOCOL=/(?<protocol>HTTP\/[0-9]\.[0-9])/
39
+ URL=/(?<url>[^ ]+)/
40
+ REFERER=/(?<referer>[^ ]+)/
41
+ RETURN_CODE=/(?<status>[1-5][0-9][0-9])/
42
+ SIZE=/(?<size>[0-9]+|-)/
43
+
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
+ hash = @format.match(line) || raise("Apache LogLine Parser Error: Could not parse #{line}")
54
+ end
55
+ end
56
+ end
@@ -1,4 +1,3 @@
1
- require 'apache_log/parser'
2
1
  require 'sqlite3'
3
2
  require 'browser'
4
3
 
@@ -50,21 +49,20 @@ module LogSense
50
49
  platform_version)
51
50
  values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
52
51
 
53
- parser = ApacheLog::Parser.new(options[:format] || 'combined')
52
+ parser = ApacheLogLineParser.new
54
53
 
55
54
  content.each do |line|
56
55
  begin
57
56
  hash = parser.parse line
58
-
59
57
  ua = Browser.new(hash[:user_agent], accept_language: "en-us")
60
58
  ins.execute(
61
- hash[:datetime].iso8601,
62
- hash[:remote_host],
63
- hash[:user],
64
- hash[:datetime].strftime("%Y-%m-%d") + " " + hash[:remote_host] + " " + hash[:user_agent],
65
- hash[:request][:method],
66
- hash[:request][:path],
67
- (hash[:request][:path] ? File.extname(hash[:request][:path]) : ""),
59
+ DateTime.parse("#{hash[:date]}T#{hash[:time]}").iso8601,
60
+ hash[:ip],
61
+ hash[:userid],
62
+ unique_visitor_id(hash),
63
+ hash[:method],
64
+ hash[:url],
65
+ (hash[:url] ? File.extname(hash[:url]) : ""),
68
66
  hash[:status],
69
67
  hash[:size].to_i,
70
68
  hash[:referer],
@@ -75,13 +73,17 @@ module LogSense
75
73
  (ua.platform.name || ""),
76
74
  (ua.platform.version || "")
77
75
  )
78
- rescue
79
- STDERR.puts "Apache Log parser error: could not parse #{line}"
76
+ rescue StandardError => e
77
+ STDERR.puts e.message
80
78
  end
81
79
  end
82
80
 
83
81
  db
84
82
  end
85
83
 
84
+ def self.unique_visitor_id hash
85
+ "#{hash[:date]} #{hash[:ip]} #{hash[:user_agent]}"
86
+ end
87
+
86
88
  end
87
89
  end
@@ -14,11 +14,11 @@
14
14
  <%= data[:log_size] %> <span class="stats-list-label">Total Entries</span>
15
15
  </li>
16
16
  <li class="stats-list-negative">
17
- <%= data[:selfpolls_size] %> <span class="stats-list-label">Self Polls Entries</span>
17
+ <%= data[:selfpolls_size] %> <span class="stats-list-label">Self Polls</span>
18
18
  </li>
19
19
  <li class="stats-list-negative">
20
20
  <td><%= data[:crawlers_size] %></td>
21
- <span class="stats-list-label">Crawlers Entries</span>
21
+ <span class="stats-list-label">Crawlers</span>
22
22
  </li>
23
23
  </ul>
24
24
 
@@ -4,22 +4,26 @@ def slugify string
4
4
  end
5
5
  %>
6
6
 
7
- <table id="<%= slugify(title || "") %>-table" class="table unstriped data-table <%= slugify(title || "") %>">
7
+ <table id="table-<%= index %>" class="table unstriped">
8
8
  <thead>
9
9
  <tr>
10
- <% header.each do |heading| %>
11
- <th class="<%= slugify(heading) %>"><%= heading %></th>
10
+ <% report[:header].each do |header| %>
11
+ <th><%= header %></th>
12
12
  <% end %>
13
13
  </tr>
14
14
  </thead>
15
15
  <tbody>
16
- <% rows.each do |row| %>
17
- <tr>
18
- <% row.each_with_index do |cell, i| %>
19
- <td class="<%= slugify (header[i] || "") %>"><%= cell %></td>
20
- <% end %>
21
- </tr>
22
- <% end %>
23
16
  </tbody>
24
17
  </table>
25
-
18
+ <script>
19
+ $(document).ready(function(){
20
+ $('#table-<%= index %>').dataTable({
21
+ data: data_<%= index %>,
22
+ columns: [
23
+ <% report[:header].each do |header| %>
24
+ { data: '<%= header %>', className: '<%= slugify(header) %>' },
25
+ <% end %>
26
+ ]
27
+ });
28
+ });
29
+ </script>
@@ -0,0 +1,12 @@
1
+ <script>
2
+ /* this is used both by Vega and DataTable */
3
+ data_<%= index %> = [
4
+ <% report[:rows].each do |row| %>
5
+ {
6
+ <% report[:header].each_with_index do |h, i| %>
7
+ "<%= h %>": <%= (row[i].class == Integer or row[i].class == Float) ? row[i] : "\"#{row[i]}\"" %>,
8
+ <% end %>
9
+ },
10
+ <% end %>
11
+ ]
12
+ </script>
@@ -17,6 +17,8 @@
17
17
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.4/dist/css/foundation.min.css">
18
18
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.css"/>
19
19
 
20
+ <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
21
+ <script type="text/javascript" src="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.js"></script>
20
22
 
21
23
  <script src="https://cdn.jsdelivr.net/npm/vega@5.21.0"></script>
22
24
  <script src="https://cdn.jsdelivr.net/npm/vega-lite@5.2.0"></script>
@@ -29,14 +31,14 @@
29
31
  }
30
32
 
31
33
  #offCanvas {
32
- color: white;
33
- background: #0D0630;
34
+ color: #DEDEDE;
35
+ background: #1C1C1C;
34
36
  border-right: none;
35
37
  box-shadow: none;
36
38
  padding: 0.5rem;
37
39
  }
38
40
  #offCanvas a {
39
- color: #E6F9AF;
41
+ color: #FFFFFF;
40
42
  }
41
43
 
42
44
  .contents-button {
@@ -69,7 +71,7 @@
69
71
 
70
72
  .card-divider {
71
73
  padding: 0.2rem 0.4rem 0.2rem 0.4rem;
72
- background: #0d0630;
74
+ background: #1C1C1C;
73
75
  color: white;
74
76
  }
75
77
 
@@ -224,24 +226,93 @@
224
226
  header: ["Day", "DOW", "Hits", "Visits", "Size"],
225
227
  rows: data[:daily_distribution],
226
228
  vega_spec: {
227
- "mark": {
228
- "type": "line",
229
- "point": {
230
- "filled": false,
231
- "fill": "white"
232
- }
233
- },
229
+ "layer": [
230
+ {
231
+ "mark": {
232
+ "type": "line",
233
+ "point": {
234
+ "filled": false,
235
+ "fill": "white"
236
+ }
237
+ },
238
+ "encoding": {
239
+ "y": {"field": "Hits", "type": "quantitative"}
240
+ }
241
+ },
242
+ {
243
+ "mark": {
244
+ "type": "text",
245
+ "color": "#3E5772",
246
+ "align": "middle",
247
+ "baseline": "top",
248
+ "dx": -10,
249
+ "yOffset": -15
250
+ },
251
+ "encoding": {
252
+ "text": {"field": "Hits", "type": "quantitative"},
253
+ "y": {"field": "Hits", "type": "quantitative"}
254
+ }
255
+ },
256
+
257
+ {
258
+ "mark": {
259
+ "type": "line",
260
+ "color": "#A52A2A",
261
+ "point": {
262
+ "color": "#A52A2A",
263
+ "filled": false,
264
+ "fill": "white",
265
+ }
266
+ },
267
+ "encoding": {
268
+ "y": {"field": "Visits", "type": "quantitative"}
269
+ }
270
+ },
271
+
272
+ {
273
+ "mark": {
274
+ "type": "text",
275
+ "color": "#A52A2A",
276
+ "align": "middle",
277
+ "baseline": "top",
278
+ "dx": -10,
279
+ "yOffset": -15
280
+ },
281
+ "encoding": {
282
+ "text": {"field": "Visits", "type": "quantitative"},
283
+ "y": {"field": "Visits", "type": "quantitative"}
284
+ }
285
+ },
286
+
287
+ ],
234
288
  "encoding": {
235
289
  "x": {"field": "Day", "type": "temporal"},
236
- "y": {"field": "Hits", "type": "quantitative"}
237
290
  }
238
291
  }
292
+
239
293
  },
240
294
  { title: "Time Distribution",
241
295
  header: ["Hour", "Hits", "Visits", "Size"],
242
296
  rows: data[:time_distribution],
243
297
  vega_spec: {
244
- "mark": "bar",
298
+ "layer": [
299
+ {
300
+ "mark": "bar"
301
+ },
302
+ {
303
+ "mark": {
304
+ "type": "text",
305
+ "align": "middle",
306
+ "baseline": "top",
307
+ "dx": -10,
308
+ "yOffset": -15
309
+ },
310
+ "encoding": {
311
+ "text": {"field": "Hits", "type": "quantitative"},
312
+ "y": {"field": "Hits", "type": "quantitative"}
313
+ }
314
+ },
315
+ ],
245
316
  "encoding": {
246
317
  "x": {"field": "Hour", "type": "nominal"},
247
318
  "y": {"field": "Hits", "type": "quantitative"}
@@ -299,7 +370,21 @@
299
370
  header: ["Browser", "Hits", "Visits", "Size"],
300
371
  rows: data[:browsers],
301
372
  vega_spec: {
302
- "mark": "bar",
373
+ "layer": [
374
+ { "mark": "bar" },
375
+ {
376
+ "mark": {
377
+ "type": "text",
378
+ "align": "middle",
379
+ "baseline": "top",
380
+ "dx": -10,
381
+ "yOffset": -15
382
+ },
383
+ "encoding": {
384
+ "text": {"field": "Hits", "type": "quantitative"},
385
+ }
386
+ },
387
+ ],
303
388
  "encoding": {
304
389
  "x": {"field": "Browser", "type": "nominal"},
305
390
  "y": {"field": "Hits", "type": "quantitative"}
@@ -310,7 +395,21 @@
310
395
  header: ["Platform", "Hits", "Visits", "Size"],
311
396
  rows: data[:platforms],
312
397
  vega_spec: {
313
- "mark": "bar",
398
+ "layer": [
399
+ { "mark": "bar" },
400
+ {
401
+ "mark": {
402
+ "type": "text",
403
+ "align": "middle",
404
+ "baseline": "top",
405
+ "dx": -10,
406
+ "yOffset": -15
407
+ },
408
+ "encoding": {
409
+ "text": {"field": "Hits", "type": "quantitative"},
410
+ }
411
+ },
412
+ ],
314
413
  "encoding": {
315
414
  "x": {"field": "Platform", "type": "nominal"},
316
415
  "y": {"field": "Hits", "type": "quantitative"}
@@ -325,11 +424,11 @@
325
424
  <% @reports.each_with_index do |report, index| %>
326
425
  <article class="card cell <%= report[:col] || "small-12 large-6" %>" >
327
426
  <div class="card-divider">
328
- <h2>
427
+ <h2 id="<%= report[:title].downcase.gsub(' ', '-') %>">
329
428
  <%= report[:title] %>
330
429
  </h2>
331
430
  </div>
332
-
431
+ <%= render "report_data.html.erb", report: report, index: index %>
333
432
  <% if report[:vega_spec] %>
334
433
  <div id="<%= "plot-#{index}" %>"></div>
335
434
  <script>
@@ -339,22 +438,15 @@
339
438
  width: "container",
340
439
  description: "<%= report[:title] %>",
341
440
  data: {
342
- values: [
343
- <% report[:rows].each do |row| %>
344
- {
345
- <% report[:header].each_with_index do |h, i| %>
346
- "<%= h %>": <%= (row[i].class == Integer or row[i].class == Float) ? row[i] : "\"#{row[i]}\"" %>,
347
- <% end %>
348
- },
349
- <% end %>
350
- ]
441
+ values: data_<%= index %>
351
442
  },
352
443
  });
353
444
  vegaEmbed('#<%= "plot-#{index}"%>', plot_spec_<%= index %>);
354
445
  </script>
355
446
  <% end %>
356
- <div class="card-section">
357
- <%= render "output_table.html.erb", report %>
447
+
448
+ <div class="card-section">
449
+ <%= render "output_table.html.erb", report: report, index: index %>
358
450
  </div>
359
451
  </article>
360
452
  <% end %>
@@ -472,9 +564,7 @@
472
564
  </section>
473
565
  </div>
474
566
 
475
- <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
476
567
  <script type="text/javascript" src="js/vendor/what-input.js"></script>
477
- <script type="text/javascript" src="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.js"></script>
478
568
  <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
479
569
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.4/dist/js/foundation.min.js" crossorigin="anonymous"></script>
480
570
  <script>
@@ -17,6 +17,8 @@
17
17
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.4/dist/css/foundation.min.css">
18
18
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.css"/>
19
19
 
20
+ <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
21
+ <script type="text/javascript" src="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.js"></script>
20
22
 
21
23
  <script src="https://cdn.jsdelivr.net/npm/vega@5.21.0"></script>
22
24
  <script src="https://cdn.jsdelivr.net/npm/vega-lite@5.2.0"></script>
@@ -29,14 +31,14 @@
29
31
  }
30
32
 
31
33
  #offCanvas {
32
- color: white;
33
- background: #0D0630;
34
+ color: #CECECE;
35
+ background: #BD000D;
34
36
  border-right: none;
35
37
  box-shadow: none;
36
38
  padding: 0.5rem;
37
39
  }
38
40
  #offCanvas a {
39
- color: #E6F9AF;
41
+ color: #FFFFFF;
40
42
  }
41
43
 
42
44
  .contents-button {
@@ -218,24 +220,55 @@
218
220
  header: ["Day", "DOW", "Hits"],
219
221
  rows: data[:daily_distribution],
220
222
  vega_spec: {
221
- "mark": {
222
- "type": "line",
223
- "point": {
224
- "filled": false,
225
- "fill": "white"
226
- }
227
- },
228
223
  "encoding": {
229
224
  "x": {"field": "Day", "type": "temporal"},
230
225
  "y": {"field": "Hits", "type": "quantitative"}
231
- }
226
+ },
227
+ "layer": [
228
+ {
229
+ "mark": {
230
+ "type": "line",
231
+ "point": {
232
+ "filled": false,
233
+ "fill": "white"
234
+ }
235
+ }
236
+ },
237
+ {
238
+ "mark": {
239
+ "type": "text",
240
+ "align": "left",
241
+ "baseline": "middle",
242
+ "dx": 5
243
+ },
244
+ "encoding": {
245
+ "text": {"field": "Hits", "type": "quantitative"}
246
+ }
247
+ }
248
+ ]
232
249
  }
233
250
  },
234
251
  { title: "Time Distribution",
235
252
  header: ["Hour", "Hits"],
236
253
  rows: data[:time_distribution],
237
254
  vega_spec: {
238
- "mark": "bar",
255
+ "layer": [
256
+ {
257
+ "mark": "bar",
258
+ },
259
+ {
260
+ "mark": {
261
+ "type": "text",
262
+ "align": "middle",
263
+ "baseline": "top",
264
+ "dx": -10,
265
+ "yOffset": -15
266
+ },
267
+ "encoding": {
268
+ "text": {"field": "Hits", "type": "quantitative"}
269
+ }
270
+ }
271
+ ],
239
272
  "encoding": {
240
273
  "x": {"field": "Hour", "type": "nominal"},
241
274
  "y": {"field": "Hits", "type": "quantitative"}
@@ -246,7 +279,23 @@
246
279
  header: ["Status", "Count"],
247
280
  rows: data[:statuses],
248
281
  vega_spec: {
249
- "mark": "bar",
282
+ "layer": [
283
+ {
284
+ "mark": "bar"
285
+ },
286
+ {
287
+ "mark": {
288
+ "type": "text",
289
+ "align": "left",
290
+ "baseline": "top",
291
+ "dx": -10,
292
+ "yOffset": -20
293
+ },
294
+ "encoding": {
295
+ "text": {"field": "Count", "type": "quantitative"}
296
+ }
297
+ }
298
+ ],
250
299
  "encoding": {
251
300
  "x": {"field": "Status", "type": "nominal"},
252
301
  "y": {"field": "Count", "type": "quantitative"}
@@ -257,7 +306,11 @@
257
306
  header: ['Controller', 'Hits', 'Min', 'Avg', 'Max'],
258
307
  rows: @data[:performance],
259
308
  vega_spec: {
260
- "mark": "point",
309
+ "layer": [
310
+ {
311
+ "mark": "point"
312
+ },
313
+ ],
261
314
  "encoding": {
262
315
  "x": {"field": "Avg", "type": "quantitative"},
263
316
  "y": {"field": "Hits", "type": "quantitative"}
@@ -286,11 +339,11 @@
286
339
  <% @reports.each_with_index do |report, index| %>
287
340
  <article class="card cell <%= report[:col] || "small-12 large-6" %>" >
288
341
  <div class="card-divider">
289
- <h2>
342
+ <h2 id="<%= report[:title].downcase.gsub(' ', '-') %>">
290
343
  <%= report[:title] %>
291
344
  </h2>
292
345
  </div>
293
-
346
+ <%= render "report_data.html.erb", report: report, index: index %>
294
347
  <% if report[:vega_spec] %>
295
348
  <div id="<%= "plot-#{index}" %>"></div>
296
349
  <script>
@@ -300,22 +353,14 @@
300
353
  width: "container",
301
354
  description: "<%= report[:title] %>",
302
355
  data: {
303
- values: [
304
- <% report[:rows].each do |row| %>
305
- {
306
- <% report[:header].each_with_index do |h, i| %>
307
- "<%= h %>": <%= (row[i].class == Integer or row[i].class == Float) ? row[i] : "\"#{row[i]}\"" %>,
308
- <% end %>
309
- },
310
- <% end %>
311
- ]
356
+ values: data_<%= index %>
312
357
  },
313
358
  });
314
359
  vegaEmbed('#<%= "plot-#{index}"%>', plot_spec_<%= index %>);
315
360
  </script>
316
361
  <% end %>
317
362
  <div class="card-section">
318
- <%= render "output_table.html.erb", report %>
363
+ <%= render "output_table.html.erb", report: report, index: index %>
319
364
  </div>
320
365
  </article>
321
366
  <% end %>
@@ -341,9 +386,7 @@
341
386
  </section>
342
387
  </div>
343
388
 
344
- <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
345
389
  <script type="text/javascript" src="js/vendor/what-input.js"></script>
346
- <script type="text/javascript" src="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.js"></script>
347
390
  <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
348
391
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.4/dist/js/foundation.min.js" crossorigin="anonymous"></script>
349
392
  <script>
@@ -1,3 +1,3 @@
1
1
  module LogSense
2
- VERSION = "1.2.2"
2
+ VERSION = "1.3.2"
3
3
  end
data/lib/log_sense.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'log_sense/version'
2
2
  require 'log_sense/options_parser'
3
+ require 'log_sense/apache_log_line_parser'
3
4
  require 'log_sense/apache_log_parser'
4
5
  require 'log_sense/apache_data_cruncher'
5
6
  require 'log_sense/rails_log_parser'
data/log_sense.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.description = %q{Generate analystics in HTML, txt, and SQLite format from an Apache and Rails log files.}
11
11
  spec.homepage = "https://www.ict4g.net/gitea/adolfo/log_sense"
12
12
  spec.license = "MIT"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.6.9")
14
14
 
15
15
  spec.metadata["allowed_push_host"] = "https://rubygems.org/"
16
16
 
@@ -27,7 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
29
 
30
- spec.add_dependency "apache_log-parser"
31
30
  spec.add_dependency "browser"
32
31
  spec.add_dependency "ipaddr"
33
32
  spec.add_dependency "iso_country_codes"
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: log_sense
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adolfo Villafiorita
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-21 00:00:00.000000000 Z
11
+ date: 2022-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: apache_log-parser
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: browser
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -138,12 +124,14 @@ files:
138
124
  - LICENSE.txt
139
125
  - README.org
140
126
  - Rakefile
127
+ - apache-screenshot.png
141
128
  - bin/console
142
129
  - bin/setup
143
130
  - exe/log_sense
144
131
  - ip_locations/dbip-country-lite.sqlite3
145
132
  - lib/log_sense.rb
146
133
  - lib/log_sense/apache_data_cruncher.rb
134
+ - lib/log_sense/apache_log_line_parser.rb
147
135
  - lib/log_sense/apache_log_parser.rb
148
136
  - lib/log_sense/emitter.rb
149
137
  - lib/log_sense/ip_locator.rb
@@ -156,6 +144,7 @@ files:
156
144
  - lib/log_sense/templates/_output_table.html.erb
157
145
  - lib/log_sense/templates/_performance.html.erb
158
146
  - lib/log_sense/templates/_performance.txt.erb
147
+ - lib/log_sense/templates/_report_data.html.erb
159
148
  - lib/log_sense/templates/_summary.html.erb
160
149
  - lib/log_sense/templates/_summary.txt.erb
161
150
  - lib/log_sense/templates/apache.html.erb
@@ -182,14 +171,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
182
171
  requirements:
183
172
  - - ">="
184
173
  - !ruby/object:Gem::Version
185
- version: 2.3.0
174
+ version: 2.6.9
186
175
  required_rubygems_version: !ruby/object:Gem::Requirement
187
176
  requirements:
188
177
  - - ">="
189
178
  - !ruby/object:Gem::Version
190
179
  version: '0'
191
180
  requirements: []
192
- rubygems_version: 3.2.32
181
+ rubygems_version: 3.0.3.1
193
182
  signing_key:
194
183
  specification_version: 4
195
184
  summary: Generate analytics from an Apache and Rails log file.