log_sense 1.0.10 → 1.1.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 +4 -4
- data/CHANGELOG.org +6 -19
- data/README.org +23 -20
- data/lib/log_sense/emitter.rb +1 -0
- data/lib/log_sense/options_parser.rb +4 -0
- data/lib/log_sense/rails_data_cruncher.rb +4 -0
- data/lib/log_sense/rails_log_parser.rb +2 -2
- data/lib/log_sense/templates/_log_structure.html.erb +24 -0
- data/lib/log_sense/templates/_performance.html.erb +21 -23
- data/lib/log_sense/templates/_summary.html.erb +23 -34
- data/lib/log_sense/templates/apache.html.erb +341 -95
- data/lib/log_sense/templates/rails.html.erb +347 -0
- data/lib/log_sense/version.rb +1 -1
- metadata +4 -3
- data/lib/log_sense/templates/_total_hits.html.erb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d20e6ee8a3f63c660de0bb9943255ed4c19b1da9e895bc269ff40310ec8f8796
|
4
|
+
data.tar.gz: 38338745e085de9b8ab82231dab82405f2933584d7d1dca4886b692a4460e2c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbc95e9b65119822687af5a20ca6ea25958b5d427b2e7ca128ac0513c20faebe4f3cd00dcabfde7dc216def72526de67bf21b4afa87883101f96144e842ad7e1
|
7
|
+
data.tar.gz: dd2faf369c1f0bcf10ed151215e4521eacec4c1d46d5aa616f34158f33f03e24e515a0677c0cf9905c11359ffebeefb96fac868ed5b717358359527cedb39691
|
data/CHANGELOG.org
CHANGED
@@ -2,26 +2,13 @@
|
|
2
2
|
#+AUTHOR: Adolfo Villafiorita
|
3
3
|
#+STARTUP: showall
|
4
4
|
|
5
|
-
*
|
5
|
+
* Changes in log_sense 1.1.2
|
6
|
+
<2021-12-17 Fri>
|
6
7
|
|
7
|
-
|
8
|
+
- Added Rails Log HTML output
|
8
9
|
|
9
|
-
|
10
|
+
* Changes in log_sense 1.1.1 and earlier
|
11
|
+
<2021-12-17 Fri>
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
** Documentation
|
14
|
-
|
15
|
-
** Code
|
16
|
-
|
17
|
-
|
18
|
-
* Version 1.0.0
|
19
|
-
|
20
|
-
** New Functions and Changes
|
21
|
-
|
22
|
-
** Fixes
|
23
|
-
|
24
|
-
** Documentation
|
25
|
-
|
26
|
-
** Code
|
13
|
+
- In the Git commit messages (not very informative, I am afraid).
|
27
14
|
|
data/README.org
CHANGED
@@ -14,21 +14,15 @@ and [[https://umami.is/][Umami]], focusing on privacy and data-ownership: the da
|
|
14
14
|
generated by LogSense is stored on your computer and owned by
|
15
15
|
you (like it should be).
|
16
16
|
|
17
|
-
LogSense is also inspired by static websites generators
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
installation headaches.
|
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.
|
22
21
|
|
23
22
|
We have, for instance, a cron job running on our servers, generating
|
24
23
|
statistics at night. The generated files are then made available on a
|
25
24
|
private area on the web.
|
26
25
|
|
27
|
-
Statistics are generated from Apache log formats in the =combined=
|
28
|
-
format and from Rails logs. Reports are tailored, but not limited, to
|
29
|
-
web servers serving static websites. No need to install Java Script
|
30
|
-
code on your websites, no cookies installed, no user tracking.
|
31
|
-
|
32
26
|
LogSense reports the following data:
|
33
27
|
|
34
28
|
- Visitors, hits, unique visitors, bandwidth used
|
@@ -62,20 +56,29 @@ LogSense generates HTML, txt (Org Mode), and SQLite outputs.
|
|
62
56
|
|
63
57
|
#+RESULTS:
|
64
58
|
#+begin_example
|
65
|
-
Usage:
|
66
|
-
|
59
|
+
Usage: log_sense [options] [logfile]
|
60
|
+
--title=TITLE Title to use in the report
|
61
|
+
-f, --input-format=FORMAT Input format (either rails or apache)
|
62
|
+
-i, --input-file=INPUT_FILE Input file
|
63
|
+
-t, --output-format=FORMAT Output format: html, org, txt, sqlite. See below for available formats
|
64
|
+
-o, --output-file=OUTPUT_FILE Output file
|
67
65
|
-b, --begin=DATE Consider entries after or on DATE
|
68
66
|
-e, --end=DATE Consider entries before or on DATE
|
69
|
-
-
|
70
|
-
-
|
71
|
-
|
72
|
-
-u, --prefix=PREFIX Prefix to add to all plots (used to run multiple analyses in the same dir)
|
73
|
-
-w, --suffix=SUFFIX Suffix to add to all plots (used to run multiple analyses in the same dir)
|
74
|
-
-c, --code-export=WHAT Control :export directive in Org Mode code blocks (code, results, *both*, none)
|
75
|
-
-f, --format=FORMAT Output format: html, org, sqlite. Defaults to org mode
|
67
|
+
-l, --limit=N Number of entries to show (defaults to 30)
|
68
|
+
-c, --crawlers=POLICY Decide what to do with crawlers (applies to Apache Logs)
|
69
|
+
-n, --no-selfpolls Ignore self poll entries (requests from ::1; applies to Apache Logs)
|
76
70
|
-v, --version Prints version information
|
77
71
|
-h, --help Prints this help
|
78
|
-
|
72
|
+
|
73
|
+
This is version 1.1.1
|
74
|
+
|
75
|
+
Output formats
|
76
|
+
apache parsing can produce the following outputs:
|
77
|
+
- sqlite
|
78
|
+
- html
|
79
|
+
rails parsing can produce the following outputs:
|
80
|
+
- sqlite
|
81
|
+
- txt
|
79
82
|
#+end_example
|
80
83
|
|
81
84
|
* Change Log
|
data/lib/log_sense/emitter.rb
CHANGED
@@ -14,6 +14,10 @@ module LogSense
|
|
14
14
|
opt_parser = OptionParser.new do |opts|
|
15
15
|
opts.banner = "Usage: log_sense [options] [logfile]"
|
16
16
|
|
17
|
+
opts.on("-tTITLE", "--title=TITLE", String, "Title to use in the report") do |n|
|
18
|
+
args[:title] = n
|
19
|
+
end
|
20
|
+
|
17
21
|
opts.on("-fFORMAT", "--input-format=FORMAT", String, "Input format (either rails or apache)") do |n|
|
18
22
|
args[:input_format] = n
|
19
23
|
end
|
@@ -26,6 +26,10 @@ module LogSense
|
|
26
26
|
@log_size = db.execute "SELECT count(started_at) from Event"
|
27
27
|
@log_size = @log_size[0][0]
|
28
28
|
|
29
|
+
# TODO: I should make the names of events/size/etc uniform betweeen Apache and Rails Logs
|
30
|
+
# SAME AS ABOVE
|
31
|
+
@total_hits = @log_size
|
32
|
+
|
29
33
|
# SAME AS ABOVE (but log_size is wrong in the case of Rails
|
30
34
|
# logs, since an event takes more than one line)
|
31
35
|
@events = db.execute "SELECT count(started_at) from Event"
|
@@ -289,8 +289,8 @@ module LogSense
|
|
289
289
|
end
|
290
290
|
|
291
291
|
# generate a unique visitor id from an event
|
292
|
-
def unique_visitor_id event
|
293
|
-
"#{DateTime.parse(event[:started_at] || event[:ended_at] || "1970-01-01").strftime("%Y-%m-%d")} #{event[:ip]}"
|
292
|
+
def self.unique_visitor_id event
|
293
|
+
"#{DateTime.parse(event[:started_at] || event[:ended_at] || "1970-01-01").strftime("%Y-%m-%d")} #{event[:ip]}"
|
294
294
|
end
|
295
295
|
|
296
296
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<ul class="stats-list">
|
2
|
+
<li>
|
3
|
+
<%= data[:first_day].strftime("%b %d, %Y") %>
|
4
|
+
<span class="stats-list-label">From</span>
|
5
|
+
</li>
|
6
|
+
<li>
|
7
|
+
<%= data[:last_day].strftime("%b %d, %Y") %>
|
8
|
+
<span class="stats-list-label">To</span>
|
9
|
+
</li>
|
10
|
+
<li class="stats-list-positive">
|
11
|
+
<%= data[:total_days] %> <span class="stats-list-label">Days in Log</span>
|
12
|
+
</li>
|
13
|
+
<li class="stats-list-negative">
|
14
|
+
<%= data[:log_size] %> <span class="stats-list-label">Total Entries</span>
|
15
|
+
</li>
|
16
|
+
<li class="stats-list-negative">
|
17
|
+
<%= data[:selfpolls_size] %> <span class="stats-list-label">Self Polls Entries</span>
|
18
|
+
</li>
|
19
|
+
<li class="stats-list-negative">
|
20
|
+
<td><%= data[:crawlers_size] %></td>
|
21
|
+
<span class="stats-list-label">Crawlers Entries</span>
|
22
|
+
</li>
|
23
|
+
</ul>
|
24
|
+
|
@@ -1,23 +1,21 @@
|
|
1
|
-
<
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
<
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
<
|
20
|
-
|
21
|
-
|
22
|
-
</tbody>
|
23
|
-
</table>
|
1
|
+
<ul class="stats-list">
|
2
|
+
<li>
|
3
|
+
<%= data[:started_at].strftime("%b %d, %Y @ %H:%M:%S") %>
|
4
|
+
<span class="stats-list-label">Analysis Started</span>
|
5
|
+
</li>
|
6
|
+
<li>
|
7
|
+
<%= data[:ended_at].strftime("%b %d, %Y @ %H:%M:%S") %>
|
8
|
+
<span class="stats-list-label">Analysis Ended</span>
|
9
|
+
</li>
|
10
|
+
<li class="stats-list-negative">
|
11
|
+
<%= "%02d:%02d" % [data[:duration] / 60, data[:duration] % 60] %>
|
12
|
+
<span class="stats-list-label">Duration</span>
|
13
|
+
</li>
|
14
|
+
<li class="stats-list-negative">
|
15
|
+
<%= data[:log_size] %> <span class="stats-list-label">Events</span>
|
16
|
+
</li>
|
17
|
+
<li class="stats-list-positive">
|
18
|
+
<td><%= "%.2f" % (data[:log_size] / data[:duration]) %>
|
19
|
+
<span class="stats-list-label">Parsed Events/sec</span>
|
20
|
+
</li>
|
21
|
+
</ul>
|
@@ -1,34 +1,23 @@
|
|
1
|
-
<
|
2
|
-
<
|
3
|
-
|
4
|
-
<
|
5
|
-
</
|
6
|
-
<
|
7
|
-
|
8
|
-
<
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
<td class="unique-visits"><%= data[:total_unique_visits] %></td>
|
25
|
-
</tr>
|
26
|
-
<tr>
|
27
|
-
<th class="avg-hits-per-unique-visits">Unique Visits</th>
|
28
|
-
<td class="avg-hits-per-unique-visits"><%= data[:total_unique_visits] != 0 ? data[:total_hits] / data[:total_unique_visits] : "N/A" %></td>
|
29
|
-
</tr>
|
30
|
-
<tr>
|
31
|
-
<th class="tx">Tx</th>
|
32
|
-
<td class="tx"><%= data[:total_size] %></td>
|
33
|
-
</tr>
|
34
|
-
</table>
|
1
|
+
<ul class="stats-list">
|
2
|
+
<li>
|
3
|
+
<%= data[:first_day_in_analysis].strftime("%b %d, %Y") %>
|
4
|
+
<span class="stats-list-label">From</span>
|
5
|
+
</li>
|
6
|
+
<li>
|
7
|
+
<%= data[:last_day_in_analysis].strftime("%b %d, %Y") %>
|
8
|
+
<span class="stats-list-label">To</span>
|
9
|
+
</li>
|
10
|
+
<li class="stats-list-positive">
|
11
|
+
<%= data[:total_days_in_analysis] %> <span class="stats-list-label">Days</span>
|
12
|
+
</li>
|
13
|
+
<li class="stats-list-negative">
|
14
|
+
<%= data[:total_hits] %> <span class="stats-list-label">Hits</span>
|
15
|
+
</li>
|
16
|
+
<li class="stats-list-negative">
|
17
|
+
<%= data[:total_unique_visits] %> <span class="stats-list-label">Unique Visits</span>
|
18
|
+
</li>
|
19
|
+
<li class="stats-list-negative">
|
20
|
+
<%= data[:total_unique_visits] != 0 ? data[:total_hits] / data[:total_unique_visits] : "N/A" %>
|
21
|
+
<span class="stats-list-label">Unique Visits / Day</span>
|
22
|
+
</li>
|
23
|
+
</ul>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!doctype html>
|
2
2
|
<html class="no-js" lang="en">
|
3
3
|
<head>
|
4
|
-
<title
|
4
|
+
<title><%= options[:title] || "Log Sense: #{data[:log_file]}" %></title>
|
5
5
|
|
6
6
|
<meta charset="utf-8" />
|
7
7
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
@@ -9,20 +9,34 @@
|
|
9
9
|
<meta name="author" content="Log Sense">
|
10
10
|
<meta name="description" content="Analysis of <%= data[:log_file] %>">
|
11
11
|
|
12
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
13
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
14
|
+
<link href="https://fonts.googleapis.com/css2?family=PT+Sans&display=swap" rel="stylesheet">
|
15
|
+
|
12
16
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css">
|
13
17
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.4/dist/css/foundation.min.css">
|
14
18
|
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.css"/>
|
15
19
|
|
20
|
+
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/vega@5.21.0"></script>
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5.2.0"></script>
|
23
|
+
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6.20.2"></script>
|
24
|
+
|
16
25
|
<style>
|
26
|
+
body {
|
27
|
+
font-family: 'PT Sans', sans-serif;
|
28
|
+
font-size: 80%;
|
29
|
+
}
|
30
|
+
|
17
31
|
#offCanvas {
|
18
32
|
color: white;
|
19
|
-
background: #
|
20
|
-
border-right: ;
|
33
|
+
background: #0D0630;
|
34
|
+
border-right: none;
|
21
35
|
box-shadow: none;
|
22
36
|
padding: 0.5rem;
|
23
37
|
}
|
24
38
|
#offCanvas a {
|
25
|
-
color:
|
39
|
+
color: #E6F9AF;
|
26
40
|
}
|
27
41
|
|
28
42
|
.contents-button {
|
@@ -33,6 +47,100 @@
|
|
33
47
|
margin-left: 45px;
|
34
48
|
}
|
35
49
|
|
50
|
+
h1 {
|
51
|
+
font-size: 1.8rem;
|
52
|
+
}
|
53
|
+
|
54
|
+
h2 {
|
55
|
+
font-size: 1.2rem;
|
56
|
+
}
|
57
|
+
|
58
|
+
th {
|
59
|
+
padding: 0.2rem 1.2rem 0.2rem 0.2rem !important
|
60
|
+
}
|
61
|
+
|
62
|
+
td {
|
63
|
+
padding: 0.2rem 1rem 0.2rem 0.2rem !important;
|
64
|
+
}
|
65
|
+
|
66
|
+
.hits, .visits, .size, .count, .s2xx, .s3xx, .so4xx, .total-hits, .total-visits {
|
67
|
+
text-align: right !important;
|
68
|
+
}
|
69
|
+
|
70
|
+
.card-divider {
|
71
|
+
padding: 0.2rem 0.4rem 0.2rem 0.4rem;
|
72
|
+
background: #0d0630;
|
73
|
+
color: white;
|
74
|
+
}
|
75
|
+
|
76
|
+
input, select {
|
77
|
+
font-size: 0.8rem !important;
|
78
|
+
height: 1.5rem !important;
|
79
|
+
padding: 0.2rem 0.4rem 0.2rem 0.4rem !important;
|
80
|
+
}
|
81
|
+
|
82
|
+
.dataTables_info {
|
83
|
+
font-size: small;
|
84
|
+
color: rgb(202, 202, 202);
|
85
|
+
}
|
86
|
+
|
87
|
+
ul.pagination, li.paginate_button {
|
88
|
+
font-size: small;
|
89
|
+
margin-top: 0px !important;
|
90
|
+
margin-bottom: 0px !important;
|
91
|
+
padding-top: 0px !important;
|
92
|
+
padding-bottom: 0px !important;
|
93
|
+
}
|
94
|
+
|
95
|
+
.stats-list {
|
96
|
+
list-style-type: none;
|
97
|
+
clear: left;
|
98
|
+
margin: 0;
|
99
|
+
padding: 0;
|
100
|
+
text-align: center;
|
101
|
+
margin-bottom: 30px;
|
102
|
+
}
|
103
|
+
|
104
|
+
.stats-list .stats-list-positive {
|
105
|
+
color: #228b22;
|
106
|
+
}
|
107
|
+
|
108
|
+
.stats-list .stats-list-negative {
|
109
|
+
color: #a52a2a;
|
110
|
+
}
|
111
|
+
|
112
|
+
.stats-list > li {
|
113
|
+
display: inline-block;
|
114
|
+
margin-right: 10px;
|
115
|
+
padding-right: 10px;
|
116
|
+
border-right: 1px solid #cacaca;
|
117
|
+
text-align: center;
|
118
|
+
font-size: 1.1em;
|
119
|
+
font-weight: bold;
|
120
|
+
}
|
121
|
+
|
122
|
+
.stats-list > li:last-child {
|
123
|
+
border: none;
|
124
|
+
margin: 0;
|
125
|
+
padding: 0;
|
126
|
+
}
|
127
|
+
|
128
|
+
.stats-list > li .stats-list-label {
|
129
|
+
display: block;
|
130
|
+
margin-top: 2px;
|
131
|
+
font-size: 0.9em;
|
132
|
+
font-weight: normal;
|
133
|
+
}
|
134
|
+
|
135
|
+
#streaks-table .ip {
|
136
|
+
vertical-align: top;
|
137
|
+
}
|
138
|
+
#streaks-table .date {
|
139
|
+
font-weight: bold;
|
140
|
+
}
|
141
|
+
#streaks-table .res-title {
|
142
|
+
font-decoration: underline;
|
143
|
+
}
|
36
144
|
</style>
|
37
145
|
|
38
146
|
</head>
|
@@ -87,123 +195,261 @@
|
|
87
195
|
</div>
|
88
196
|
|
89
197
|
<section class="main-section">
|
90
|
-
<h1
|
198
|
+
<h1><%= options[:title] || "Log Sense: #{data[:log_file]}" %></h1>
|
199
|
+
|
200
|
+
<p><b>Input File:</b> <%= (data[:log_file] || "stdin") %></p>
|
91
201
|
|
92
202
|
<div class="grid-x grid-margin-x">
|
93
|
-
<article class="small-12 large-6 cell">
|
94
|
-
<
|
95
|
-
|
203
|
+
<article class="card small-12 large-6 cell">
|
204
|
+
<div class="card-divider">
|
205
|
+
<h2 id="summary">Summary</h2>
|
206
|
+
</div>
|
207
|
+
<div class="card-section">
|
208
|
+
<%= render "summary.html.erb", data: data %>
|
209
|
+
</div>
|
96
210
|
</article>
|
97
211
|
|
98
|
-
<article class="cell small-12 large-6">
|
99
|
-
<
|
100
|
-
|
212
|
+
<article class="card cell small-12 large-6">
|
213
|
+
<div class="card-divider">
|
214
|
+
<h2 id="log-structure">Log Structure</h2>
|
215
|
+
</div>
|
216
|
+
<div class="card-section">
|
217
|
+
<%= render "log_structure.html.erb", data: data %>
|
218
|
+
</div>
|
101
219
|
</article>
|
102
220
|
</div>
|
103
221
|
|
104
222
|
<% @reports = [
|
105
|
-
{ title: "Daily Distribution",
|
106
|
-
|
107
|
-
|
223
|
+
{ title: "Daily Distribution",
|
224
|
+
header: ["Day", "DOW", "Hits", "Visits", "Size"],
|
225
|
+
rows: data[:daily_distribution],
|
226
|
+
vega_spec: {
|
227
|
+
"mark": {
|
228
|
+
"type": "line",
|
229
|
+
"point": {
|
230
|
+
"filled": false,
|
231
|
+
"fill": "white"
|
232
|
+
}
|
233
|
+
},
|
234
|
+
"encoding": {
|
235
|
+
"x": {"field": "Day", "type": "temporal"},
|
236
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
},
|
240
|
+
{ title: "Time Distribution",
|
241
|
+
header: ["Hour", "Hits", "Visits", "Size"],
|
242
|
+
rows: data[:time_distribution],
|
243
|
+
vega_spec: {
|
244
|
+
"mark": "bar",
|
245
|
+
"encoding": {
|
246
|
+
"x": {"field": "Hour", "type": "nominal"},
|
247
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
248
|
+
}
|
249
|
+
}
|
250
|
+
},
|
251
|
+
{ title: "Most Requested Pages",
|
252
|
+
header: ["Path", "Hits", "Visits", "Size"],
|
253
|
+
rows: data[:most_requested_pages],
|
254
|
+
},
|
108
255
|
{ title: "Most Requested Resources", header: ["Path", "Hits", "Visits", "Size"], rows: data[:most_requested_resources] },
|
109
256
|
{ title: "404 on HTML Files", header: ["Path", "Hits", "Visits"], rows: data[:missed_pages] },
|
110
257
|
{ title: "404 on other Resources", header: ["Path", "Hits", "Visits"], rows: data[:missed_resources] },
|
111
|
-
{ title: "Attacks", header: ["Path", "Hits", "Visits"], rows: data[:attacks] },
|
112
|
-
{ title: "Statuses",
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
258
|
+
{ title: "Attacks", header: ["Path", "Hits", "Visits"], rows: data[:attacks], col: "small-12 cell" },
|
259
|
+
{ title: "Statuses",
|
260
|
+
header: ["Status", "Count"],
|
261
|
+
rows: data[:statuses],
|
262
|
+
vega_spec: {
|
263
|
+
"mark": "bar",
|
264
|
+
"encoding": {
|
265
|
+
"x": {"field": "Status", "type": "nominal"},
|
266
|
+
"y": {"field": "Count", "type": "quantitative"}
|
267
|
+
}
|
268
|
+
}
|
269
|
+
},
|
270
|
+
{ title: "Daily Statuses",
|
271
|
+
header: ["Date", "S_2xx", "S_3xx", "S_4xx"],
|
272
|
+
rows: data[:statuses_by_day],
|
273
|
+
vega_spec: {
|
274
|
+
"transform": [ {"fold": ["S_2xx", "S_3xx", "S_4xx" ] }],
|
275
|
+
"mark": "bar",
|
276
|
+
"encoding": {
|
277
|
+
"x": {
|
278
|
+
"field": "Date",
|
279
|
+
"type": "ordinal",
|
280
|
+
"timeUnit": "day",
|
281
|
+
},
|
282
|
+
"y": {
|
283
|
+
"aggregate": "sum",
|
284
|
+
"field": "value",
|
285
|
+
"type": "quantitative"
|
286
|
+
},
|
287
|
+
"color": {
|
288
|
+
"field": "key",
|
289
|
+
"type": "nominal",
|
290
|
+
"scale": {
|
291
|
+
"domain": ["S_2xx", "S_3xx", "S_4xx"],
|
292
|
+
"range": ["#228b22", "#ff8c00", "#a52a2a"]
|
293
|
+
},
|
294
|
+
}
|
295
|
+
}
|
296
|
+
}
|
297
|
+
},
|
298
|
+
{ title: "Browsers",
|
299
|
+
header: ["Browser", "Hits", "Visits", "Size"],
|
300
|
+
rows: data[:browsers],
|
301
|
+
vega_spec: {
|
302
|
+
"mark": "bar",
|
303
|
+
"encoding": {
|
304
|
+
"x": {"field": "Browser", "type": "nominal"},
|
305
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
306
|
+
}
|
307
|
+
}
|
308
|
+
},
|
309
|
+
{ title: "Platforms",
|
310
|
+
header: ["Platform", "Hits", "Visits", "Size"],
|
311
|
+
rows: data[:platforms],
|
312
|
+
vega_spec: {
|
313
|
+
"mark": "bar",
|
314
|
+
"encoding": {
|
315
|
+
"x": {"field": "Platform", "type": "nominal"},
|
316
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
317
|
+
}
|
318
|
+
}
|
319
|
+
},
|
117
320
|
{ title: "IPs", header: ["IPs", "Hits", "Visits", "Size", "Country"], rows: data[:ips] },
|
321
|
+
{ title: "Referers", header: ["Referers", "Hits", "Visits", "Size"], rows: data[:referers], col: "small-12 cell" },
|
118
322
|
]
|
119
323
|
%>
|
120
324
|
<div class="grid-x grid-margin-x">
|
121
|
-
<% @reports.
|
122
|
-
<article class="cell <%= report[:col] || "small-12 large-6" %>" >
|
123
|
-
<
|
124
|
-
|
125
|
-
|
126
|
-
|
325
|
+
<% @reports.each_with_index do |report, index| %>
|
326
|
+
<article class="card cell <%= report[:col] || "small-12 large-6" %>" >
|
327
|
+
<div class="card-divider">
|
328
|
+
<h2>
|
329
|
+
<%= report[:title] %>
|
330
|
+
</h2>
|
331
|
+
</div>
|
332
|
+
|
333
|
+
<% if report[:vega_spec] %>
|
334
|
+
<div id="<%= "plot-#{index}" %>"></div>
|
335
|
+
<script>
|
336
|
+
plot_spec_<%= index %> = Object.assign(
|
337
|
+
<%= report[:vega_spec].to_json %>,
|
338
|
+
{ "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
339
|
+
width: "container",
|
340
|
+
description: "<%= report[:title] %>",
|
341
|
+
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
|
+
]
|
351
|
+
},
|
352
|
+
});
|
353
|
+
vegaEmbed('#<%= "plot-#{index}"%>', plot_spec_<%= index %>);
|
354
|
+
</script>
|
355
|
+
<% end %>
|
356
|
+
<div class="card-section">
|
357
|
+
<%= render "output_table.html.erb", report %>
|
358
|
+
</div>
|
127
359
|
</article>
|
128
360
|
<% end %>
|
129
361
|
</div>
|
130
362
|
|
131
|
-
<article>
|
132
|
-
<
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
<th>Total Visits</th>
|
139
|
-
<th>IPs</th>
|
140
|
-
</tr>
|
141
|
-
</thead>
|
142
|
-
<tbody>
|
143
|
-
<%# IP, Hits, Visits Size, Country%>
|
144
|
-
<% data[:ips].group_by { |x| x[4] }.each do |k, v| %>
|
363
|
+
<article class="card">
|
364
|
+
<div class="card-divider">
|
365
|
+
<h2 id="geolocation">Geolocation</h2>
|
366
|
+
</div>
|
367
|
+
<div class="card-section">
|
368
|
+
<table id="geolocation-table" class="table unstriped">
|
369
|
+
<thead>
|
145
370
|
<tr>
|
146
|
-
<
|
147
|
-
<
|
148
|
-
<
|
149
|
-
<
|
150
|
-
</tr>
|
151
|
-
|
152
|
-
|
153
|
-
|
371
|
+
<th>Country Code</th>
|
372
|
+
<th>Total Hits</th>
|
373
|
+
<th>Total Visits</th>
|
374
|
+
<th>IPs</th>
|
375
|
+
</tr>
|
376
|
+
</thead>
|
377
|
+
<tbody>
|
378
|
+
<%# IP, Hits, Visits Size, Country%>
|
379
|
+
<% data[:ips].group_by { |x| x[4] }.each do |k, v| %>
|
380
|
+
<tr>
|
381
|
+
<td class="country"><%= k %></td>
|
382
|
+
<td class="total-hits"><%= v.map { |x| x[1] }.inject(&:+) %></td>
|
383
|
+
<td class="total-visits"><%= v.map { |x| x[2] }.inject(&:+) %></td>
|
384
|
+
<td class="ips">
|
385
|
+
<%= v.map { |x| "<a href=\"https://whatismyipaddress.com/ip/#{x[0]}\">#{x[0]}</a>" }.join(", ") %>
|
386
|
+
</td>
|
387
|
+
</tr>
|
388
|
+
<% end %>
|
389
|
+
</tbody>
|
390
|
+
</table>
|
391
|
+
</div>
|
154
392
|
</article>
|
155
393
|
|
156
|
-
<article>
|
157
|
-
<
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
<th>
|
164
|
-
<div class="grid-x grid-margin-x">
|
165
|
-
<div class="col-2 cell">
|
166
|
-
Day
|
167
|
-
</div>
|
168
|
-
<div class="col-10 cell">
|
169
|
-
Resources
|
170
|
-
</div>
|
171
|
-
</div>
|
172
|
-
</th>
|
173
|
-
</tr>
|
174
|
-
</thead>
|
175
|
-
<tbody>
|
176
|
-
<% data[:streaks].group_by(&:first).each do |ip, date_urls| %>
|
394
|
+
<article class="card">
|
395
|
+
<div class="card-divider">
|
396
|
+
<h2 id="streaks">Streaks</h2>
|
397
|
+
</div>
|
398
|
+
<div class="card-section">
|
399
|
+
<table id="streaks-table" class="table data-table streaks">
|
400
|
+
<thead>
|
177
401
|
<tr>
|
178
|
-
<
|
179
|
-
<
|
402
|
+
<th>IP</th>
|
403
|
+
<th>
|
180
404
|
<div class="grid-x grid-margin-x">
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
<ul>
|
188
|
-
<% urls.map { |x| x[2] }.compact.select { |x| x.match /.*\.html?/ }.each do |url| %>
|
189
|
-
<li><%= url %></li>
|
190
|
-
<% end %>
|
191
|
-
</ul>
|
192
|
-
|
193
|
-
<span class="res-title">Other Resources:</span>
|
194
|
-
<ul>
|
195
|
-
<% urls.map { |x| x[2] }.compact.sort.select { |x| x and not x.match /.*\.html?/ }.each do |url| %>
|
196
|
-
<li><%= url %></li>
|
197
|
-
<% end %>
|
198
|
-
</ul>
|
199
|
-
</div>
|
200
|
-
<% end %>
|
405
|
+
<div class="col-2 cell">
|
406
|
+
Day
|
407
|
+
</div>
|
408
|
+
<div class="col-10 cell">
|
409
|
+
Resources
|
410
|
+
</div>
|
201
411
|
</div>
|
202
|
-
</
|
412
|
+
</th>
|
203
413
|
</tr>
|
204
|
-
|
205
|
-
|
206
|
-
|
414
|
+
</thead>
|
415
|
+
<tbody>
|
416
|
+
<% data[:streaks].group_by(&:first).each do |ip, date_urls| %>
|
417
|
+
<tr>
|
418
|
+
<td class="ip">
|
419
|
+
<a href="https://whatismyipaddress.com/ip/<%= ip %>"><%= ip %></a>
|
420
|
+
</td>
|
421
|
+
<td class="streaks">
|
422
|
+
<div class="grid-x grid-margin-x">
|
423
|
+
<% date_urls.group_by { |x| x[1] }.each do |date, urls| %>
|
424
|
+
<div class="col-2 cell">
|
425
|
+
<span class="date"><%= date %></span>
|
426
|
+
</div>
|
427
|
+
<div class="col-10 cell grid-x">
|
428
|
+
<div class="small-12 medium-6 cell">
|
429
|
+
<span class="res-title">HTML:</span>
|
430
|
+
<ul>
|
431
|
+
<% urls.map { |x| x[2] }.compact.select { |x| x.match /.*\.html?/ }.each do |url| %>
|
432
|
+
<li><%= url %></li>
|
433
|
+
<% end %>
|
434
|
+
</ul>
|
435
|
+
</div>
|
436
|
+
<div class=" small-12 medium-6 cell">
|
437
|
+
<span class="res-title small-12 medium-6 cell">Other Resources:</span>
|
438
|
+
<ul>
|
439
|
+
<% urls.map { |x| x[2] }.compact.sort.select { |x| x and not x.match /.*\.html?/ }.each do |url| %>
|
440
|
+
<li><%= url %></li>
|
441
|
+
<% end %>
|
442
|
+
</ul>
|
443
|
+
</div>
|
444
|
+
</div>
|
445
|
+
<% end %>
|
446
|
+
</div>
|
447
|
+
</td>
|
448
|
+
</tr>
|
449
|
+
<% end %>
|
450
|
+
</tbody>
|
451
|
+
</table>
|
452
|
+
</div>
|
207
453
|
</article>
|
208
454
|
|
209
455
|
<div class="grid-x grid-margin-x">
|
@@ -0,0 +1,347 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html class="no-js" lang="en">
|
3
|
+
<head>
|
4
|
+
<title><%= options[:title] || "Log Sense: #{data[:log_file]}" %></title>
|
5
|
+
|
6
|
+
<meta charset="utf-8" />
|
7
|
+
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
9
|
+
<meta name="author" content="Log Sense">
|
10
|
+
<meta name="description" content="Analysis of <%= data[:log_file] %>">
|
11
|
+
|
12
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
13
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
14
|
+
<link href="https://fonts.googleapis.com/css2?family=PT+Sans&display=swap" rel="stylesheet">
|
15
|
+
|
16
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css">
|
17
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.4/dist/css/foundation.min.css">
|
18
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.css"/>
|
19
|
+
|
20
|
+
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/vega@5.21.0"></script>
|
22
|
+
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5.2.0"></script>
|
23
|
+
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6.20.2"></script>
|
24
|
+
|
25
|
+
<style>
|
26
|
+
body {
|
27
|
+
font-family: 'PT Sans', sans-serif;
|
28
|
+
font-size: 80%;
|
29
|
+
}
|
30
|
+
|
31
|
+
#offCanvas {
|
32
|
+
color: white;
|
33
|
+
background: #0D0630;
|
34
|
+
border-right: none;
|
35
|
+
box-shadow: none;
|
36
|
+
padding: 0.5rem;
|
37
|
+
}
|
38
|
+
#offCanvas a {
|
39
|
+
color: #E6F9AF;
|
40
|
+
}
|
41
|
+
|
42
|
+
.contents-button {
|
43
|
+
font-size: xx-large;
|
44
|
+
}
|
45
|
+
|
46
|
+
.main-section {
|
47
|
+
margin-left: 45px;
|
48
|
+
}
|
49
|
+
|
50
|
+
h1 {
|
51
|
+
font-size: 1.8rem;
|
52
|
+
}
|
53
|
+
|
54
|
+
h2 {
|
55
|
+
font-size: 1.2rem;
|
56
|
+
}
|
57
|
+
|
58
|
+
th {
|
59
|
+
padding: 0.2rem 1.2rem 0.2rem 0.2rem !important
|
60
|
+
}
|
61
|
+
|
62
|
+
td {
|
63
|
+
padding: 0.2rem 1rem 0.2rem 0.2rem !important;
|
64
|
+
}
|
65
|
+
|
66
|
+
.hits, .visits, .size, .count, .s2xx, .s3xx, .so4xx, .total-hits, .total-visits {
|
67
|
+
text-align: right !important;
|
68
|
+
}
|
69
|
+
|
70
|
+
.card-divider {
|
71
|
+
padding: 0.2rem 0.4rem 0.2rem 0.4rem;
|
72
|
+
background: #0d0630;
|
73
|
+
color: white;
|
74
|
+
}
|
75
|
+
|
76
|
+
input, select {
|
77
|
+
font-size: 0.8rem !important;
|
78
|
+
height: 1.5rem !important;
|
79
|
+
padding: 0.2rem 0.4rem 0.2rem 0.4rem !important;
|
80
|
+
}
|
81
|
+
|
82
|
+
.dataTables_info {
|
83
|
+
font-size: small;
|
84
|
+
color: rgb(202, 202, 202);
|
85
|
+
}
|
86
|
+
|
87
|
+
ul.pagination, li.paginate_button {
|
88
|
+
font-size: small;
|
89
|
+
margin-top: 0px !important;
|
90
|
+
margin-bottom: 0px !important;
|
91
|
+
padding-top: 0px !important;
|
92
|
+
padding-bottom: 0px !important;
|
93
|
+
}
|
94
|
+
|
95
|
+
.stats-list {
|
96
|
+
list-style-type: none;
|
97
|
+
clear: left;
|
98
|
+
margin: 0;
|
99
|
+
padding: 0;
|
100
|
+
text-align: center;
|
101
|
+
margin-bottom: 30px;
|
102
|
+
}
|
103
|
+
|
104
|
+
.stats-list .stats-list-positive {
|
105
|
+
color: #228b22;
|
106
|
+
}
|
107
|
+
|
108
|
+
.stats-list .stats-list-negative {
|
109
|
+
color: #a52a2a;
|
110
|
+
}
|
111
|
+
|
112
|
+
.stats-list > li {
|
113
|
+
display: inline-block;
|
114
|
+
margin-right: 10px;
|
115
|
+
padding-right: 10px;
|
116
|
+
border-right: 1px solid #cacaca;
|
117
|
+
text-align: center;
|
118
|
+
font-size: 1.1em;
|
119
|
+
font-weight: bold;
|
120
|
+
}
|
121
|
+
|
122
|
+
.stats-list > li:last-child {
|
123
|
+
border: none;
|
124
|
+
margin: 0;
|
125
|
+
padding: 0;
|
126
|
+
}
|
127
|
+
|
128
|
+
.stats-list > li .stats-list-label {
|
129
|
+
display: block;
|
130
|
+
margin-top: 2px;
|
131
|
+
font-size: 0.9em;
|
132
|
+
font-weight: normal;
|
133
|
+
}
|
134
|
+
|
135
|
+
#streaks-table .ip {
|
136
|
+
vertical-align: top;
|
137
|
+
}
|
138
|
+
#streaks-table .date {
|
139
|
+
font-weight: bold;
|
140
|
+
}
|
141
|
+
#streaks-table .res-title {
|
142
|
+
font-decoration: underline;
|
143
|
+
}
|
144
|
+
</style>
|
145
|
+
|
146
|
+
</head>
|
147
|
+
|
148
|
+
<body>
|
149
|
+
<div class="off-canvas-wrapper">
|
150
|
+
<div class="off-canvas position-left" id="offCanvas" data-off-canvas>
|
151
|
+
<nav>
|
152
|
+
<h2>Navigation</h2>
|
153
|
+
<ul class="no-bullet">
|
154
|
+
<% [
|
155
|
+
"Summary",
|
156
|
+
"Log Structure",
|
157
|
+
"Daily Distribution",
|
158
|
+
"Time Distribution",
|
159
|
+
"Most Requested Pages",
|
160
|
+
"Most Requested Resources",
|
161
|
+
"404 on HTML Files",
|
162
|
+
"404 on other Resources",
|
163
|
+
"Attacks",
|
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>
|
188
|
+
</div>
|
189
|
+
<div class="off-canvas-content grid-container grid-x fluid" data-off-canvas-content>
|
190
|
+
<div data-sticky-container>
|
191
|
+
<div class="sticky" data-sticky data-margin-top="0">
|
192
|
+
<div class="contents-button">
|
193
|
+
<i id="hamburger" class="fi-list" data-toggle="offCanvas"></i>
|
194
|
+
</div>
|
195
|
+
</div>
|
196
|
+
</div>
|
197
|
+
|
198
|
+
<section class="main-section">
|
199
|
+
<h1><%= options[:title] || "Log Sense: #{data[:log_file]}" %></h1>
|
200
|
+
|
201
|
+
<p><b>Input File:</b> <%= (data[:log_file] || "stdin") %></p>
|
202
|
+
|
203
|
+
<div class="grid-x grid-margin-x">
|
204
|
+
<article class="card small-12 large-6 cell">
|
205
|
+
<div class="card-divider">
|
206
|
+
<h2 id="summary">Summary</h2>
|
207
|
+
</div>
|
208
|
+
<div class="card-section">
|
209
|
+
<%= render "summary.html.erb", data: data %>
|
210
|
+
</div>
|
211
|
+
</article>
|
212
|
+
|
213
|
+
<article class="card cell small-12 large-6">
|
214
|
+
<div class="card-divider">
|
215
|
+
<h2 id="log-structure">Log Structure</h2>
|
216
|
+
</div>
|
217
|
+
<div class="card-section">
|
218
|
+
<%= render "log_structure.html.erb", data: data %>
|
219
|
+
</div>
|
220
|
+
</article>
|
221
|
+
</div>
|
222
|
+
|
223
|
+
<% @reports = [
|
224
|
+
{ title: "Daily Distribution",
|
225
|
+
header: ["Day", "DOW", "Hits"],
|
226
|
+
rows: data[:daily_distribution],
|
227
|
+
vega_spec: {
|
228
|
+
"mark": {
|
229
|
+
"type": "line",
|
230
|
+
"point": {
|
231
|
+
"filled": false,
|
232
|
+
"fill": "white"
|
233
|
+
}
|
234
|
+
},
|
235
|
+
"encoding": {
|
236
|
+
"x": {"field": "Day", "type": "temporal"},
|
237
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
},
|
241
|
+
{ title: "Time Distribution",
|
242
|
+
header: ["Hour", "Hits"],
|
243
|
+
rows: data[:time_distribution],
|
244
|
+
vega_spec: {
|
245
|
+
"mark": "bar",
|
246
|
+
"encoding": {
|
247
|
+
"x": {"field": "Hour", "type": "nominal"},
|
248
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
},
|
252
|
+
{ title: "Statuses",
|
253
|
+
header: ["Status", "Count"],
|
254
|
+
rows: data[:statuses],
|
255
|
+
vega_spec: {
|
256
|
+
"mark": "bar",
|
257
|
+
"encoding": {
|
258
|
+
"x": {"field": "Status", "type": "nominal"},
|
259
|
+
"y": {"field": "Count", "type": "quantitative"}
|
260
|
+
}
|
261
|
+
}
|
262
|
+
},
|
263
|
+
{ title: "Rails Performance",
|
264
|
+
header: ['Controller', 'Hits', 'Min', 'Avg', 'Max'],
|
265
|
+
rows: @data[:performance] },
|
266
|
+
{ title: "Fatal Events",
|
267
|
+
header: ['Date', 'IP', 'URL', 'Log ID'], rows: @data[:fatal]
|
268
|
+
},
|
269
|
+
{ title: "IPs", header: ["IPs", "Hits", "Country"], rows: data[:ips] },
|
270
|
+
]
|
271
|
+
%>
|
272
|
+
<div class="grid-x grid-margin-x">
|
273
|
+
<% @reports.each_with_index do |report, index| %>
|
274
|
+
<article class="card cell <%= report[:col] || "small-12 large-6" %>" >
|
275
|
+
<div class="card-divider">
|
276
|
+
<h2>
|
277
|
+
<%= report[:title] %>
|
278
|
+
</h2>
|
279
|
+
</div>
|
280
|
+
|
281
|
+
<% if report[:vega_spec] %>
|
282
|
+
<div id="<%= "plot-#{index}" %>"></div>
|
283
|
+
<script>
|
284
|
+
plot_spec_<%= index %> = Object.assign(
|
285
|
+
<%= report[:vega_spec].to_json %>,
|
286
|
+
{ "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
|
287
|
+
width: "container",
|
288
|
+
description: "<%= report[:title] %>",
|
289
|
+
data: {
|
290
|
+
values: [
|
291
|
+
<% report[:rows].each do |row| %>
|
292
|
+
{
|
293
|
+
<% report[:header].each_with_index do |h, i| %>
|
294
|
+
"<%= h %>": <%= (row[i].class == Integer or row[i].class == Float) ? row[i] : "\"#{row[i]}\"" %>,
|
295
|
+
<% end %>
|
296
|
+
},
|
297
|
+
<% end %>
|
298
|
+
]
|
299
|
+
},
|
300
|
+
});
|
301
|
+
vegaEmbed('#<%= "plot-#{index}"%>', plot_spec_<%= index %>);
|
302
|
+
</script>
|
303
|
+
<% end %>
|
304
|
+
<div class="card-section">
|
305
|
+
<%= render "output_table.html.erb", report %>
|
306
|
+
</div>
|
307
|
+
</article>
|
308
|
+
<% end %>
|
309
|
+
</div>
|
310
|
+
|
311
|
+
<div class="grid-x grid-margin-x">
|
312
|
+
<div class="cell small-12 large-6">
|
313
|
+
<article>
|
314
|
+
<h2 id="command-invocation">Command Invocation</h2>
|
315
|
+
|
316
|
+
<%= render "command_invocation.html.erb", data: data, options: options %>
|
317
|
+
</article>
|
318
|
+
</div>
|
319
|
+
|
320
|
+
<div class="small-12 large-6 cell">
|
321
|
+
<article>
|
322
|
+
<h2 id="performance"> Performance</h2>
|
323
|
+
|
324
|
+
<%= render "performance.html.erb", data: data %>
|
325
|
+
</article>
|
326
|
+
</div>
|
327
|
+
</div>
|
328
|
+
</section>
|
329
|
+
</div>
|
330
|
+
|
331
|
+
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
332
|
+
<script type="text/javascript" src="js/vendor/what-input.js"></script>
|
333
|
+
<script type="text/javascript" src="https://cdn.datatables.net/v/zf/dt-1.11.3/datatables.min.js"></script>
|
334
|
+
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
|
335
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.4/dist/js/foundation.min.js" crossorigin="anonymous"></script>
|
336
|
+
<script>
|
337
|
+
$(document).foundation();
|
338
|
+
|
339
|
+
$(document).ready(function () {
|
340
|
+
$('.data-table').each(function () {
|
341
|
+
$(this).DataTable();
|
342
|
+
});
|
343
|
+
});
|
344
|
+
</script>
|
345
|
+
</div>
|
346
|
+
</body>
|
347
|
+
</html>
|
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.1.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-
|
11
|
+
date: 2021-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: apache_log-parser
|
@@ -152,13 +152,14 @@ files:
|
|
152
152
|
- lib/log_sense/rails_log_parser.rb
|
153
153
|
- lib/log_sense/templates/_command_invocation.html.erb
|
154
154
|
- lib/log_sense/templates/_command_invocation.txt.erb
|
155
|
+
- lib/log_sense/templates/_log_structure.html.erb
|
155
156
|
- lib/log_sense/templates/_output_table.html.erb
|
156
157
|
- lib/log_sense/templates/_performance.html.erb
|
157
158
|
- lib/log_sense/templates/_performance.txt.erb
|
158
159
|
- lib/log_sense/templates/_summary.html.erb
|
159
160
|
- lib/log_sense/templates/_summary.txt.erb
|
160
|
-
- lib/log_sense/templates/_total_hits.html.erb
|
161
161
|
- lib/log_sense/templates/apache.html.erb
|
162
|
+
- lib/log_sense/templates/rails.html.erb
|
162
163
|
- lib/log_sense/templates/rails.txt.erb
|
163
164
|
- lib/log_sense/version.rb
|
164
165
|
- log_sense.gemspec
|
@@ -1,32 +0,0 @@
|
|
1
|
-
<table class="table unstriped log-structure">
|
2
|
-
<tbody>
|
3
|
-
<tr>
|
4
|
-
<th>Input file</th>
|
5
|
-
<td><b><%= (data[:log_file] || "stdin") %></b></td>
|
6
|
-
</tr>
|
7
|
-
<tr>
|
8
|
-
<th>Period in Log</th>
|
9
|
-
<td><%= data[:first_day] %> -- <%= data[:last_day] %></td>
|
10
|
-
</tr>
|
11
|
-
<tr>
|
12
|
-
<th>Total days</th>
|
13
|
-
<td><%= data[:total_days] %></td>
|
14
|
-
</tr>
|
15
|
-
<tr>
|
16
|
-
<th>Log size</th>
|
17
|
-
<td><%= data[:log_size] %></td>
|
18
|
-
</tr>
|
19
|
-
<tr>
|
20
|
-
<th>Self poll entries</th>
|
21
|
-
<td><%= data[:selfpolls_size] %></td>
|
22
|
-
</tr>
|
23
|
-
<tr>
|
24
|
-
<th>Crawlers</th>
|
25
|
-
<td><%= data[:crawlers_size] %></td>
|
26
|
-
</tr>
|
27
|
-
<tr>
|
28
|
-
<th>Entries considered</th>
|
29
|
-
<td><%= data[:total_hits] %></td>
|
30
|
-
</tr>
|
31
|
-
</tbody>
|
32
|
-
</table>
|