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.
@@ -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
- vega_spec: {
20
- "layer": [
21
- {
22
- "mark": {
23
- "type": "line",
24
- "point": {
25
- "filled": false,
26
- "fill": "white"
27
- }
28
- },
29
- "encoding": {
30
- "y": {"field": "Hits", "type": "quantitative"}
31
- }
32
- },
33
- {
34
- "mark": {
35
- "type": "text",
36
- "color": "#3E5772",
37
- "align": "middle",
38
- "baseline": "top",
39
- "dx": -10,
40
- "yOffset": -15
41
- },
42
- "encoding": {
43
- "text": {"field": "Hits", "type": "quantitative"},
44
- "y": {"field": "Hits", "type": "quantitative"}
45
- }
46
- },
47
-
48
- {
49
- "mark": {
50
- "type": "line",
51
- "color": "#A52A2A",
52
- "point": {
53
- "color": "#A52A2A",
54
- "filled": false,
55
- "fill": "white",
56
- }
57
- },
58
- "encoding": {
59
- "y": {"field": "Visits", "type": "quantitative"}
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
- vega_spec: {
90
- "layer": [
91
- {
92
- "mark": "bar"
93
- },
94
- {
95
- "mark": {
96
- "type": "text",
97
- "align": "middle",
98
- "baseline": "top",
99
- "dx": -10,
100
- "yOffset": -15
101
- },
102
- "encoding": {
103
- "text": {"field": "Hits", "type": "quantitative"},
104
- "y": {"field": "Hits", "type": "quantitative"}
105
- }
106
- },
107
- ],
108
- "encoding": {
109
- "x": {"field": "Hour", "type": "nominal"},
110
- "y": {"field": "Hits", "type": "quantitative"}
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
- vega_spec: {
178
- "mark": "bar",
179
- "encoding": {
180
- "x": {"field": "Status", "type": "nominal"},
181
- "y": {"field": "Count", "type": "quantitative"}
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
- vega_spec: {
191
- "transform": [ {"fold": ["S_2xx", "S_3xx", "S_4xx", "S_5xx" ] }],
192
- "mark": "bar",
193
- "encoding": {
194
- "x": {
195
- "field": "Date",
196
- "type": "ordinal",
197
- "timeUnit": "day",
198
- },
199
- "y": {
200
- "aggregate": "sum",
201
- "field": "value",
202
- "type": "quantitative"
203
- },
204
- "color": {
205
- "field": "key",
206
- "type": "nominal",
207
- "scale": {
208
- "domain": ["S_2xx", "S_3xx", "S_4xx"],
209
- "range": ["#228b22", "#ff8c00", "#a52a2a"]
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
- vega_spec: {
221
- "layer": [
222
- { "mark": "bar" },
223
- {
224
- "mark": {
225
- "type": "text",
226
- "align": "middle",
227
- "baseline": "top",
228
- "dx": -10,
229
- "yOffset": -15
230
- },
231
- "encoding": {
232
- "text": {"field": "Hits", "type": "quantitative"},
233
- }
234
- },
235
- ],
236
- "encoding": {
237
- "x": {"field": "Browser", "type": "nominal"},
238
- "y": {"field": "Hits", "type": "quantitative"}
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
- vega_spec: {
248
- "layer": [
249
- { "mark": "bar" },
250
- {
251
- "mark": {
252
- "type": "text",
253
- "align": "middle",
254
- "baseline": "top",
255
- "dx": -10,
256
- "yOffset": -15
257
- },
258
- "encoding": {
259
- "text": {"field": "Hits", "type": "quantitative"},
260
- }
261
- },
262
- ],
263
- "encoding": {
264
- "x": {"field": "Platform", "type": "nominal"},
265
- "y": {"field": "Hits", "type": "quantitative"}
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
  {
@@ -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/vega@5.28.0",
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 = {})