log_sense 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.org +5 -0
- data/README.org +46 -46
- data/lib/log_sense/apache_report_shaper.rb +267 -163
- data/lib/log_sense/emitter.rb +1 -3
- data/lib/log_sense/rails/log_parser.rb +74 -14
- data/lib/log_sense/rails_aggregator.rb +74 -1
- data/lib/log_sense/rails_report_shaper.rb +330 -107
- data/lib/log_sense/templates/_log_structure.html.erb +6 -6
- data/lib/log_sense/templates/_performance.html.erb +6 -5
- data/lib/log_sense/templates/_rails.css.erb +14 -2
- data/lib/log_sense/templates/_stylesheet.css +26 -8
- data/lib/log_sense/templates/_summary.html.erb +8 -8
- data/lib/log_sense/templates/report_html.erb +18 -2
- data/lib/log_sense/version.rb +1 -1
- metadata +2 -2
@@ -7,6 +7,7 @@ module LogSense
|
|
7
7
|
# header: header of tabular data
|
8
8
|
# rows: data to show
|
9
9
|
# column_alignment: specification of column alignments (works for txt reports)
|
10
|
+
# echarts_spec: specifications for eCharts output
|
10
11
|
# vega_spec: specifications for Vega output
|
11
12
|
# datatable_options: specific options for datatable
|
12
13
|
def shape(data)
|
@@ -16,100 +17,84 @@ module LogSense
|
|
16
17
|
header: %w[Day DOW Hits Visits Size],
|
17
18
|
column_alignment: %i[left left right right right],
|
18
19
|
rows: data[:daily_distribution],
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
{
|
64
|
-
"mark": {
|
65
|
-
"type": "text",
|
66
|
-
"color": "#A52A2A",
|
67
|
-
"align": "middle",
|
68
|
-
"baseline": "top",
|
69
|
-
"dx": -10,
|
70
|
-
"yOffset": -15
|
71
|
-
},
|
72
|
-
"encoding": {
|
73
|
-
"text": {"field": "Visits", "type": "quantitative"},
|
74
|
-
"y": {"field": "Visits", "type": "quantitative"}
|
75
|
-
}
|
76
|
-
},
|
77
|
-
|
78
|
-
],
|
79
|
-
"encoding": {
|
80
|
-
"x": {"field": "Day", "type": "temporal"},
|
81
|
-
}
|
20
|
+
echarts_spec: "{
|
21
|
+
toolbox: {
|
22
|
+
feature: {
|
23
|
+
saveAsImage: {},
|
24
|
+
}
|
25
|
+
},
|
26
|
+
tooltip: {
|
27
|
+
trigger: 'axis'
|
28
|
+
},
|
29
|
+
legend: {
|
30
|
+
data: ['Hits', 'Visits']
|
31
|
+
},
|
32
|
+
xAxis: {
|
33
|
+
type: 'category',
|
34
|
+
data: SERIES_DATA.filter(row => row['Day'] != '').map(row => row['Day']),
|
35
|
+
showGrid: true,
|
36
|
+
},
|
37
|
+
yAxis: {
|
38
|
+
type: 'value',
|
39
|
+
name: 'Hits & Visits',
|
40
|
+
showGrid: true,
|
41
|
+
},
|
42
|
+
series: [
|
43
|
+
{
|
44
|
+
name: 'Hits',
|
45
|
+
data: SERIES_DATA.filter(row => row['Day'] != '').map(row => row['Hits']),
|
46
|
+
type: 'line',
|
47
|
+
color: '#4C78A8',
|
48
|
+
label: {
|
49
|
+
show: true,
|
50
|
+
position: 'top'
|
51
|
+
},
|
52
|
+
},
|
53
|
+
{
|
54
|
+
name: 'Visits',
|
55
|
+
data: SERIES_DATA.filter(row => row['Day'] != '').map(row => row['Visits']),
|
56
|
+
type: 'line',
|
57
|
+
color: '#D30001',
|
58
|
+
label: {
|
59
|
+
show: true,
|
60
|
+
position: 'top'
|
61
|
+
},
|
62
|
+
},
|
63
|
+
]
|
82
64
|
}
|
65
|
+
",
|
83
66
|
},
|
84
67
|
{
|
85
68
|
title: "Time Distribution",
|
86
69
|
header: %w[Hour Hits Visits Size],
|
87
70
|
column_alignment: %i[left right right right],
|
88
71
|
rows: data[:time_distribution],
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
72
|
+
echarts_spec: "{
|
73
|
+
xAxis: {
|
74
|
+
type: 'category',
|
75
|
+
data: SERIES_DATA.map(row => row['Hour'])
|
76
|
+
/* data: ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09',
|
77
|
+
'10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
|
78
|
+
'20', '21', '22', '23', '24'] */
|
79
|
+
},
|
80
|
+
yAxis: {
|
81
|
+
type: 'value'
|
82
|
+
},
|
83
|
+
tooltip: {
|
84
|
+
trigger: 'axis'
|
85
|
+
},
|
86
|
+
series: [
|
87
|
+
{
|
88
|
+
data: SERIES_DATA.map(row => row['Hits']),
|
89
|
+
type: 'bar',
|
90
|
+
color: '#4C78A8',
|
91
|
+
label: {
|
92
|
+
show: true,
|
93
|
+
position: 'top'
|
94
|
+
},
|
95
|
+
}
|
96
|
+
]
|
97
|
+
}",
|
113
98
|
},
|
114
99
|
{
|
115
100
|
title: "20_ and 30_ on HTML pages",
|
@@ -127,15 +112,15 @@ module LogSense
|
|
127
112
|
},
|
128
113
|
{
|
129
114
|
title: "40_ and 50_x on HTML pages",
|
130
|
-
header: %w[Path Hits Visits Status],
|
131
|
-
column_alignment: %i[left right right right],
|
115
|
+
header: %w[Path Hits Visits Size Status],
|
116
|
+
column_alignment: %i[left right right right right],
|
132
117
|
rows: data[:missed_pages],
|
133
118
|
datatable_options: "columnDefs: [{ width: \"40%\", targets: 0 }, { width: \"20%\", targets: [1, 2, 3] }], dataRender: true"
|
134
119
|
},
|
135
120
|
{
|
136
121
|
title: "40_ and 50_ on other resources",
|
137
|
-
header: %w[Path Hits Visits Status],
|
138
|
-
column_alignment: %i[left right right right],
|
122
|
+
header: %w[Path Hits Visits Size Status],
|
123
|
+
column_alignment: %i[left right right right right],
|
139
124
|
rows: data[:missed_resources],
|
140
125
|
datatable_options: "columnDefs: [{ width: \"40%\", targets: 0 }, { width: \"20%\", targets: [1, 2, 3] }], dataRender: true"
|
141
126
|
},
|
@@ -174,97 +159,183 @@ module LogSense
|
|
174
159
|
header: %w[Status Count],
|
175
160
|
column_alignment: %i[left right],
|
176
161
|
rows: data[:statuses],
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
162
|
+
echarts_spec: "{
|
163
|
+
xAxis: {
|
164
|
+
type: 'category',
|
165
|
+
data: SERIES_DATA.map(row => row['Status'])
|
166
|
+
/* data: ['100', '101', '102', '103',
|
167
|
+
'200', '201', '202', '203', '204', '205', '206', '207', '208', '226',
|
168
|
+
'300', '301', '302', '303', '304', '305', '306', '307', '308',
|
169
|
+
'400', '401', '402', '403', '404', '405', '406', '407', '408', '409', '410', '411', '412', '413', '414', '415', '416', '417', '418', '421', '422', '423', '424', '425', '426', '428', '429', '431', '451',
|
170
|
+
'500', '501', '502', '503', '504', '505', '506', '507', '508', '510', '511'] */
|
171
|
+
},
|
172
|
+
yAxis: {
|
173
|
+
type: 'value'
|
174
|
+
},
|
175
|
+
tooltip: {
|
176
|
+
trigger: 'axis'
|
177
|
+
},
|
178
|
+
series: [
|
179
|
+
{
|
180
|
+
data: SERIES_DATA.map(row => row['Count']),
|
181
|
+
type: 'bar',
|
182
|
+
color: '#4C78A8',
|
183
|
+
label: {
|
184
|
+
show: true,
|
185
|
+
position: 'top'
|
186
|
+
},
|
187
|
+
}
|
188
|
+
]
|
189
|
+
}",
|
184
190
|
},
|
185
191
|
{
|
186
192
|
title: "Daily Statuses",
|
187
193
|
header: %w[Date S_2xx S_3xx S_4xx S_5xx],
|
188
194
|
column_alignment: %i[left right right right right],
|
189
195
|
rows: data[:statuses_by_day],
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
196
|
+
echarts_spec: "{
|
197
|
+
xAxis: {
|
198
|
+
type: 'category',
|
199
|
+
data: SERIES_DATA.map(row => row['Date'])
|
200
|
+
},
|
201
|
+
yAxis: {
|
202
|
+
type: 'value'
|
203
|
+
},
|
204
|
+
tooltip: {
|
205
|
+
trigger: 'axis'
|
206
|
+
},
|
207
|
+
series: [
|
208
|
+
{
|
209
|
+
data: SERIES_DATA.map(row => row['S_2xx']),
|
210
|
+
type: 'bar',
|
211
|
+
color: '#218521',
|
212
|
+
stack: 'total',
|
213
|
+
label: {
|
214
|
+
show: true,
|
215
|
+
position: 'right'
|
216
|
+
},
|
217
|
+
},
|
218
|
+
{
|
219
|
+
data: SERIES_DATA.map(row => row['S_3xx']),
|
220
|
+
type: 'bar',
|
221
|
+
color: '#FF8C00',
|
222
|
+
stack: 'total',
|
223
|
+
label: {
|
224
|
+
show: true,
|
225
|
+
position: 'right'
|
226
|
+
},
|
227
|
+
},
|
228
|
+
{
|
229
|
+
data: SERIES_DATA.map(row => row['S_4xx']),
|
230
|
+
type: 'bar',
|
231
|
+
color: '#A52A2A',
|
232
|
+
stack: 'total',
|
233
|
+
label: {
|
234
|
+
show: true,
|
235
|
+
position: 'right'
|
236
|
+
},
|
237
|
+
},
|
238
|
+
{
|
239
|
+
data: SERIES_DATA.map(row => row['S_5xx']),
|
240
|
+
type: 'bar',
|
241
|
+
color: '#000000',
|
242
|
+
stack: 'total',
|
243
|
+
label: {
|
244
|
+
show: true,
|
245
|
+
position: 'right'
|
246
|
+
},
|
247
|
+
},
|
248
|
+
]
|
249
|
+
}",
|
214
250
|
},
|
215
251
|
{
|
216
252
|
title: "Browsers",
|
217
253
|
header: %w[Browser Hits Visits Size],
|
218
254
|
column_alignment: %i[left right right right],
|
219
255
|
rows: data[:browsers],
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
256
|
+
echarts_spec: "{
|
257
|
+
toolbox: {
|
258
|
+
feature: {
|
259
|
+
saveAsImage: {},
|
260
|
+
}
|
261
|
+
},
|
262
|
+
tooltip: {
|
263
|
+
trigger: 'axis'
|
264
|
+
},
|
265
|
+
xAxis: {
|
266
|
+
type: 'category',
|
267
|
+
data: SERIES_DATA.sort(order_by_name).map(row => row['Browser']),
|
268
|
+
showGrid: true,
|
269
|
+
axisLabel: {
|
270
|
+
rotate: 45 // Rotate the labels by 90 degrees
|
271
|
+
}
|
272
|
+
},
|
273
|
+
yAxis: {
|
274
|
+
type: 'value',
|
275
|
+
name: 'Browser Hits',
|
276
|
+
showGrid: true,
|
277
|
+
},
|
278
|
+
series: [
|
279
|
+
{
|
280
|
+
name: 'Hits',
|
281
|
+
data: SERIES_DATA.sort(order_by_name).map(row => row['Hits']),
|
282
|
+
type: 'bar',
|
283
|
+
color: '#4C78A8',
|
284
|
+
label: {
|
285
|
+
show: true,
|
286
|
+
position: 'top'
|
287
|
+
},
|
288
|
+
},
|
289
|
+
]
|
240
290
|
}
|
291
|
+
function order_by_name(a, b) {
|
292
|
+
return a['Browser'] < b['Browser'] ? -1 : 1
|
293
|
+
}
|
294
|
+
",
|
241
295
|
},
|
242
296
|
{
|
243
297
|
title: "Platforms",
|
244
298
|
header: %w[Platform Hits Visits Size],
|
245
299
|
column_alignment: %i[left right right right],
|
246
300
|
rows: data[:platforms],
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
301
|
+
echarts_spec: "{
|
302
|
+
toolbox: {
|
303
|
+
feature: {
|
304
|
+
saveAsImage: {},
|
305
|
+
}
|
306
|
+
},
|
307
|
+
tooltip: {
|
308
|
+
trigger: 'axis'
|
309
|
+
},
|
310
|
+
xAxis: {
|
311
|
+
type: 'category',
|
312
|
+
data: SERIES_DATA.sort(order_by_platform).map(row => row['Platform']),
|
313
|
+
showGrid: true,
|
314
|
+
axisLabel: {
|
315
|
+
rotate: 45 // Rotate the labels by 90 degrees
|
316
|
+
}
|
317
|
+
},
|
318
|
+
yAxis: {
|
319
|
+
type: 'value',
|
320
|
+
name: 'Platform Hits',
|
321
|
+
showGrid: true,
|
322
|
+
},
|
323
|
+
series: [
|
324
|
+
{
|
325
|
+
name: 'Hits',
|
326
|
+
data: SERIES_DATA.sort(order_by_platform).map(row => row['Hits']),
|
327
|
+
type: 'bar',
|
328
|
+
color: '#4C78A8',
|
329
|
+
label: {
|
330
|
+
show: true,
|
331
|
+
position: 'top'
|
332
|
+
},
|
333
|
+
},
|
334
|
+
]
|
267
335
|
}
|
336
|
+
function order_by_platform(a, b) {
|
337
|
+
return a['Platform'] < b['Platform'] ? -1 : 1
|
338
|
+
}",
|
268
339
|
},
|
269
340
|
{
|
270
341
|
title: "IPs",
|
@@ -284,7 +355,40 @@ module LogSense
|
|
284
355
|
v.map { |x| x[0] }.uniq.size,
|
285
356
|
v.map { |x| x[0] }.join(WORDS_SEPARATOR)
|
286
357
|
]
|
287
|
-
}&.sort { |x, y| y[3] <=> x[3] }
|
358
|
+
}&.sort { |x, y| y[3] <=> x[3] },
|
359
|
+
echarts_height: "600px",
|
360
|
+
echarts_spec: "{
|
361
|
+
tooltip: {
|
362
|
+
trigger: 'axis',
|
363
|
+
axisPointer: {
|
364
|
+
type: 'shadow'
|
365
|
+
}
|
366
|
+
},
|
367
|
+
xAxis: {
|
368
|
+
type: 'value',
|
369
|
+
boundaryGap: [0, 0.01]
|
370
|
+
},
|
371
|
+
yAxis: {
|
372
|
+
type: 'category',
|
373
|
+
data: SERIES_DATA.sort(order_by_hits).map(row => row['Country'] ),
|
374
|
+
},
|
375
|
+
series: [
|
376
|
+
{
|
377
|
+
type: 'bar',
|
378
|
+
data: SERIES_DATA.sort(order_by_hits).map(row => row['Hits'] ),
|
379
|
+
color: '#4C78A8',
|
380
|
+
label: {
|
381
|
+
show: true,
|
382
|
+
position: 'right'
|
383
|
+
},
|
384
|
+
},
|
385
|
+
]
|
386
|
+
};
|
387
|
+
|
388
|
+
function order_by_hits(a, b) {
|
389
|
+
return Number(a['Hits']) < Number(b['Hits']) ? -1 : 1
|
390
|
+
}
|
391
|
+
"
|
288
392
|
},
|
289
393
|
ip_per_hour_report_spec(ips_per_hour(data[:ips_per_hour])),
|
290
394
|
{
|
data/lib/log_sense/emitter.rb
CHANGED
@@ -19,9 +19,7 @@ module LogSense
|
|
19
19
|
"https://code.jquery.com/jquery-3.7.1.min.js",
|
20
20
|
"https://cdn.datatables.net/v/zf/dt-2.0.8/datatables.min.js",
|
21
21
|
"https://cdn.jsdelivr.net/npm/foundation-sites@6.8.1/dist/js/foundation.min.js",
|
22
|
-
"https://cdn.jsdelivr.net/npm/
|
23
|
-
"https://cdn.jsdelivr.net/npm/vega-lite@5.18.1",
|
24
|
-
"https://cdn.jsdelivr.net/npm/vega-embed@6.25.0"
|
22
|
+
"https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"
|
25
23
|
].freeze
|
26
24
|
|
27
25
|
def self.emit(reports = {}, data = {}, options = {})
|