log_sense 1.4.0 → 1.4.1
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 +12 -0
- data/README.org +5 -4
- data/exe/log_sense +13 -15
- data/lib/log_sense/apache_data_cruncher.rb +3 -3
- data/lib/log_sense/apache_log_line_parser.rb +9 -9
- data/lib/log_sense/apache_log_parser.rb +44 -36
- data/lib/log_sense/emitter.rb +454 -21
- data/lib/log_sense/options_parser.rb +29 -29
- data/lib/log_sense/rails_data_cruncher.rb +4 -3
- data/lib/log_sense/rails_log_parser.rb +108 -100
- data/lib/log_sense/templates/_command_invocation.html.erb +0 -4
- data/lib/log_sense/templates/_command_invocation.txt.erb +4 -3
- data/lib/log_sense/templates/_output_table.html.erb +1 -7
- data/lib/log_sense/templates/_output_table.txt.erb +13 -0
- data/lib/log_sense/templates/_performance.html.erb +1 -1
- data/lib/log_sense/templates/_performance.txt.erb +8 -5
- data/lib/log_sense/templates/_report_data.html.erb +2 -3
- data/lib/log_sense/templates/_summary.html.erb +1 -1
- data/lib/log_sense/templates/_summary.txt.erb +11 -8
- data/lib/log_sense/templates/apache.html.erb +1 -212
- data/lib/log_sense/templates/apache.txt.erb +35 -0
- data/lib/log_sense/templates/rails.html.erb +0 -137
- data/lib/log_sense/templates/rails.txt.erb +8 -57
- data/lib/log_sense/version.rb +1 -1
- metadata +4 -2
data/lib/log_sense/emitter.rb
CHANGED
@@ -4,27 +4,26 @@ require 'erb'
|
|
4
4
|
require 'ostruct'
|
5
5
|
|
6
6
|
module LogSense
|
7
|
+
#
|
8
|
+
# Emit Data
|
9
|
+
#
|
7
10
|
module Emitter
|
8
|
-
|
9
|
-
#
|
10
|
-
# Emit Data
|
11
|
-
#
|
12
11
|
def self.emit data = {}, options = {}
|
13
|
-
@input_format = options[:input_format] ||
|
14
|
-
@output_format = options[:output_format] ||
|
12
|
+
@input_format = options[:input_format] || 'apache'
|
13
|
+
@output_format = options[:output_format] || 'html'
|
15
14
|
|
16
15
|
# for the ERB binding
|
16
|
+
@reports = method("#{@input_format}_report_specification".to_sym).call(data)
|
17
17
|
@data = data
|
18
18
|
@options = options
|
19
19
|
|
20
20
|
# determine the main template to read
|
21
|
-
@template = File.join(File.dirname(__FILE__),
|
21
|
+
@template = File.join(File.dirname(__FILE__), 'templates', "#{@input_format}.#{@output_format}.erb")
|
22
22
|
erb_template = File.read @template
|
23
|
-
|
24
23
|
output = ERB.new(erb_template).result(binding)
|
25
24
|
|
26
25
|
if options[:output_file]
|
27
|
-
file = File.open options[:output_file],
|
26
|
+
file = File.open options[:output_file], 'w'
|
28
27
|
file.write output
|
29
28
|
file.close
|
30
29
|
else
|
@@ -32,33 +31,467 @@ module LogSense
|
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
35
|
-
|
34
|
+
private_class_method
|
36
35
|
|
37
36
|
def self.render(template, vars)
|
38
|
-
@template = File.join(File.dirname(__FILE__),
|
37
|
+
@template = File.join(File.dirname(__FILE__), 'templates', "_#{template}")
|
39
38
|
erb_template = File.read @template
|
40
39
|
ERB.new(erb_template).result(OpenStruct.new(vars).instance_eval { binding })
|
41
40
|
end
|
42
41
|
|
43
42
|
def self.escape_javascript(string)
|
44
43
|
js_escape_map = {
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
"\n" => '\\n',
|
52
|
-
"\r" => '\\r',
|
44
|
+
'<' => '<',
|
45
|
+
'</' => '<\/',
|
46
|
+
'\\' => '\\\\',
|
47
|
+
'\r\n' => '\\r\\n',
|
48
|
+
'\n' => '\\n',
|
49
|
+
'\r' => '\\r',
|
53
50
|
'"' => ' \\"',
|
54
51
|
"'" => " \\'",
|
55
|
-
|
56
|
-
|
52
|
+
'`' => ' \\`',
|
53
|
+
'$' => ' \\$'
|
57
54
|
}
|
58
55
|
js_escape_map.each do |k, v|
|
59
56
|
string = string.gsub(k, v)
|
60
57
|
end
|
61
58
|
string
|
62
59
|
end
|
60
|
+
|
61
|
+
def self.slugify(string)
|
62
|
+
(string.start_with?(/[0-9]/) ? 'slug-' : '') + string.downcase.gsub(' ', '-')
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.process(value)
|
66
|
+
klass = value.class
|
67
|
+
[Integer, Float].include?(klass) ? value : escape_javascript(value || '')
|
68
|
+
end
|
69
|
+
|
70
|
+
# limit width of special columns, that is, URL, Path, and Description
|
71
|
+
# - data: array of arrays
|
72
|
+
# - heading: array with column names
|
73
|
+
# - width width to set
|
74
|
+
def self.shorten(data, heading, width)
|
75
|
+
# indexes of columns which have to be shortened
|
76
|
+
to_shorten = %w[URL Description Path].map { |x| heading.index x }.compact
|
77
|
+
if width.nil? || to_shorten.empty? || data[0].nil?
|
78
|
+
data
|
79
|
+
else
|
80
|
+
table_columns = data[0].size
|
81
|
+
data.map { |x|
|
82
|
+
(0..table_columns - 1).each.map { |col|
|
83
|
+
should_shorten = x[col] && x[col].size > width - 3 && to_shorten.include?(col)
|
84
|
+
should_shorten ? "#{x[col][0..(width - 3)]}..." : x[col]
|
85
|
+
}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Specification of the reports to generate
|
92
|
+
# Array of hashes with the following information:
|
93
|
+
# - title: report_title
|
94
|
+
# header: header of tabular data
|
95
|
+
# rows: data to show
|
96
|
+
# column_alignment: specification of column alignments (works for txt reports)
|
97
|
+
# vega_spec: specifications for Vega output
|
98
|
+
# datatable_options: specific options for datatable
|
99
|
+
def self.apache_report_specification(data)
|
100
|
+
[
|
101
|
+
{ title: 'Daily Distribution',
|
102
|
+
header: %w[Day DOW Hits Visits Size],
|
103
|
+
column_alignment: %i[left left right right right],
|
104
|
+
rows: data[:daily_distribution],
|
105
|
+
vega_spec: {
|
106
|
+
'layer': [
|
107
|
+
{
|
108
|
+
'mark': {
|
109
|
+
'type': 'line',
|
110
|
+
'point': {
|
111
|
+
'filled': false,
|
112
|
+
'fill': 'white'
|
113
|
+
}
|
114
|
+
},
|
115
|
+
'encoding': {
|
116
|
+
'y': {'field': 'Hits', 'type': 'quantitative'}
|
117
|
+
}
|
118
|
+
},
|
119
|
+
{
|
120
|
+
'mark': {
|
121
|
+
'type': 'text',
|
122
|
+
'color': '#3E5772',
|
123
|
+
'align': 'middle',
|
124
|
+
'baseline': 'top',
|
125
|
+
'dx': -10,
|
126
|
+
'yOffset': -15
|
127
|
+
},
|
128
|
+
'encoding': {
|
129
|
+
'text': {'field': 'Hits', 'type': 'quantitative'},
|
130
|
+
'y': {'field': 'Hits', 'type': 'quantitative'}
|
131
|
+
}
|
132
|
+
},
|
133
|
+
|
134
|
+
{
|
135
|
+
'mark': {
|
136
|
+
'type': 'line',
|
137
|
+
'color': '#A52A2A',
|
138
|
+
'point': {
|
139
|
+
'color': '#A52A2A',
|
140
|
+
'filled': false,
|
141
|
+
'fill': 'white',
|
142
|
+
}
|
143
|
+
},
|
144
|
+
'encoding': {
|
145
|
+
'y': {'field': 'Visits', 'type': 'quantitative'}
|
146
|
+
}
|
147
|
+
},
|
148
|
+
|
149
|
+
{
|
150
|
+
'mark': {
|
151
|
+
'type': 'text',
|
152
|
+
'color': '#A52A2A',
|
153
|
+
'align': 'middle',
|
154
|
+
'baseline': 'top',
|
155
|
+
'dx': -10,
|
156
|
+
'yOffset': -15
|
157
|
+
},
|
158
|
+
'encoding': {
|
159
|
+
'text': {'field': 'Visits', 'type': 'quantitative'},
|
160
|
+
'y': {'field': 'Visits', 'type': 'quantitative'}
|
161
|
+
}
|
162
|
+
},
|
163
|
+
|
164
|
+
],
|
165
|
+
'encoding': {
|
166
|
+
'x': {'field': 'Day', 'type': 'temporal'},
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
},
|
171
|
+
{ title: 'Time Distribution',
|
172
|
+
header: %w[Hour Hits Visits Size],
|
173
|
+
column_alignment: %i[left right right right],
|
174
|
+
rows: data[:time_distribution],
|
175
|
+
vega_spec: {
|
176
|
+
'layer': [
|
177
|
+
{
|
178
|
+
'mark': 'bar'
|
179
|
+
},
|
180
|
+
{
|
181
|
+
'mark': {
|
182
|
+
'type': 'text',
|
183
|
+
'align': 'middle',
|
184
|
+
'baseline': 'top',
|
185
|
+
'dx': -10,
|
186
|
+
'yOffset': -15
|
187
|
+
},
|
188
|
+
'encoding': {
|
189
|
+
'text': {'field': 'Hits', 'type': 'quantitative'},
|
190
|
+
'y': {'field': 'Hits', 'type': 'quantitative'}
|
191
|
+
}
|
192
|
+
},
|
193
|
+
],
|
194
|
+
'encoding': {
|
195
|
+
'x': {'field': 'Hour', 'type': 'nominal'},
|
196
|
+
'y': {'field': 'Hits', 'type': 'quantitative'}
|
197
|
+
}
|
198
|
+
}
|
199
|
+
},
|
200
|
+
{
|
201
|
+
title: '20_ and 30_ on HTML pages',
|
202
|
+
header: %w[Path Hits Visits Size Status],
|
203
|
+
column_alignment: %i[left right right right right],
|
204
|
+
rows: data[:most_requested_pages],
|
205
|
+
datatable_options: 'columnDefs: [{ width: \'40%\', targets: 0 } ]'
|
206
|
+
},
|
207
|
+
{
|
208
|
+
title: '20_ and 30_ on other resources',
|
209
|
+
header: %w[Path Hits Visits Size Status],
|
210
|
+
column_alignment: %i[left right right right right],
|
211
|
+
rows: data[:most_requested_resources],
|
212
|
+
datatable_options: 'columnDefs: [{ width: \'40%\', targets: 0 } ]'
|
213
|
+
},
|
214
|
+
{
|
215
|
+
title: '40_ and 50_x on HTML pages',
|
216
|
+
header: %w[Path Hits Visits Status],
|
217
|
+
column_alignment: %i[left right right right],
|
218
|
+
rows: data[:missed_pages],
|
219
|
+
datatable_options: 'columnDefs: [{ width: \'40%\', targets: 0 } ]'
|
220
|
+
},
|
221
|
+
{
|
222
|
+
title: '40_ and 50_ on other resources',
|
223
|
+
header: %w[Path Hits Visits Status],
|
224
|
+
column_alignment: %i[left right right right],
|
225
|
+
rows: data[:missed_resources],
|
226
|
+
datatable_options: 'columnDefs: [{ width: \'40%\', targets: 0 } ]'
|
227
|
+
},
|
228
|
+
{
|
229
|
+
title: 'Statuses',
|
230
|
+
header: %w[Status Count],
|
231
|
+
column_alignment: %i[left right],
|
232
|
+
rows: data[:statuses],
|
233
|
+
vega_spec: {
|
234
|
+
'mark': 'bar',
|
235
|
+
'encoding': {
|
236
|
+
'x': {'field': 'Status', 'type': 'nominal'},
|
237
|
+
'y': {'field': 'Count', 'type': 'quantitative'}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
},
|
241
|
+
{
|
242
|
+
title: 'Daily Statuses',
|
243
|
+
header: %w[Date S_2xx S_3xx S_4xx],
|
244
|
+
column_alignment: %i[left right right right],
|
245
|
+
rows: data[:statuses_by_day],
|
246
|
+
vega_spec: {
|
247
|
+
'transform': [ {'fold': ['S_2xx', 'S_3xx', 'S_4xx' ] }],
|
248
|
+
'mark': 'bar',
|
249
|
+
'encoding': {
|
250
|
+
'x': {
|
251
|
+
'field': 'Date',
|
252
|
+
'type': 'ordinal',
|
253
|
+
'timeUnit': 'day',
|
254
|
+
},
|
255
|
+
'y': {
|
256
|
+
'aggregate': 'sum',
|
257
|
+
'field': 'value',
|
258
|
+
'type': 'quantitative'
|
259
|
+
},
|
260
|
+
'color': {
|
261
|
+
'field': 'key',
|
262
|
+
'type': 'nominal',
|
263
|
+
'scale': {
|
264
|
+
'domain': ['S_2xx', 'S_3xx', 'S_4xx'],
|
265
|
+
'range': ['#228b22', '#ff8c00', '#a52a2a']
|
266
|
+
},
|
267
|
+
}
|
268
|
+
}
|
269
|
+
}
|
270
|
+
},
|
271
|
+
{ title: 'Browsers',
|
272
|
+
header: %w[Browser Hits Visits Size],
|
273
|
+
column_alignment: %i[left right right right],
|
274
|
+
rows: data[:browsers],
|
275
|
+
vega_spec: {
|
276
|
+
'layer': [
|
277
|
+
{ 'mark': 'bar' },
|
278
|
+
{
|
279
|
+
'mark': {
|
280
|
+
'type': 'text',
|
281
|
+
'align': 'middle',
|
282
|
+
'baseline': 'top',
|
283
|
+
'dx': -10,
|
284
|
+
'yOffset': -15
|
285
|
+
},
|
286
|
+
'encoding': {
|
287
|
+
'text': {'field': 'Hits', 'type': 'quantitative'},
|
288
|
+
}
|
289
|
+
},
|
290
|
+
],
|
291
|
+
'encoding': {
|
292
|
+
'x': {'field': 'Browser', 'type': 'nominal'},
|
293
|
+
'y': {'field': 'Hits', 'type': 'quantitative'}
|
294
|
+
}
|
295
|
+
}
|
296
|
+
},
|
297
|
+
{ title: 'Platforms',
|
298
|
+
header: %w[Platform Hits Visits Size],
|
299
|
+
column_alignment: %i[left right right right],
|
300
|
+
rows: data[:platforms],
|
301
|
+
vega_spec: {
|
302
|
+
'layer': [
|
303
|
+
{ 'mark': 'bar' },
|
304
|
+
{
|
305
|
+
'mark': {
|
306
|
+
'type': 'text',
|
307
|
+
'align': 'middle',
|
308
|
+
'baseline': 'top',
|
309
|
+
'dx': -10,
|
310
|
+
'yOffset': -15
|
311
|
+
},
|
312
|
+
'encoding': {
|
313
|
+
'text': {'field': 'Hits', 'type': 'quantitative'},
|
314
|
+
}
|
315
|
+
},
|
316
|
+
],
|
317
|
+
'encoding': {
|
318
|
+
'x': {'field': 'Platform', 'type': 'nominal'},
|
319
|
+
'y': {'field': 'Hits', 'type': 'quantitative'}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
},
|
323
|
+
{
|
324
|
+
title: 'IPs',
|
325
|
+
header: %w[IPs Hits Visits Size Country],
|
326
|
+
column_alignment: %i[left right right right left],
|
327
|
+
rows: data[:ips]
|
328
|
+
},
|
329
|
+
{
|
330
|
+
title: 'Referers',
|
331
|
+
header: %w[Referers Hits Visits Size],
|
332
|
+
column_alignment: %i[left right right right],
|
333
|
+
rows: data[:referers],
|
334
|
+
col: 'small-12 cell'
|
335
|
+
},
|
336
|
+
]
|
337
|
+
end
|
338
|
+
|
339
|
+
def self.rails_report_specification(data)
|
340
|
+
[
|
341
|
+
{
|
342
|
+
title: "Daily Distribution",
|
343
|
+
header: %w[Day DOW Hits],
|
344
|
+
column_alignment: %i[left left right],
|
345
|
+
rows: data[:daily_distribution],
|
346
|
+
vega_spec: {
|
347
|
+
"encoding": {
|
348
|
+
"x": {"field": "Day", "type": "temporal"},
|
349
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
350
|
+
},
|
351
|
+
"layer": [
|
352
|
+
{
|
353
|
+
"mark": {
|
354
|
+
"type": "line",
|
355
|
+
"point": {
|
356
|
+
"filled": false,
|
357
|
+
"fill": "white"
|
358
|
+
}
|
359
|
+
}
|
360
|
+
},
|
361
|
+
{
|
362
|
+
"mark": {
|
363
|
+
"type": "text",
|
364
|
+
"align": "left",
|
365
|
+
"baseline": "middle",
|
366
|
+
"dx": 5
|
367
|
+
},
|
368
|
+
"encoding": {
|
369
|
+
"text": {"field": "Hits", "type": "quantitative"}
|
370
|
+
}
|
371
|
+
}
|
372
|
+
]
|
373
|
+
}
|
374
|
+
},
|
375
|
+
{
|
376
|
+
title: "Time Distribution",
|
377
|
+
header: %w[Hour Hits],
|
378
|
+
column_alignment: %i[left right],
|
379
|
+
rows: data[:time_distribution],
|
380
|
+
vega_spec: {
|
381
|
+
"layer": [
|
382
|
+
{
|
383
|
+
"mark": "bar",
|
384
|
+
},
|
385
|
+
{
|
386
|
+
"mark": {
|
387
|
+
"type": "text",
|
388
|
+
"align": "middle",
|
389
|
+
"baseline": "top",
|
390
|
+
"dx": -10,
|
391
|
+
"yOffset": -15
|
392
|
+
},
|
393
|
+
"encoding": {
|
394
|
+
"text": {"field": "Hits", "type": "quantitative"}
|
395
|
+
}
|
396
|
+
}
|
397
|
+
],
|
398
|
+
"encoding": {
|
399
|
+
"x": {"field": "Hour", "type": "nominal"},
|
400
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
401
|
+
}
|
402
|
+
}
|
403
|
+
},
|
404
|
+
{
|
405
|
+
title: "Statuses",
|
406
|
+
header: %w[Status Count],
|
407
|
+
column_alignment: %i[left right],
|
408
|
+
rows: data[:statuses],
|
409
|
+
vega_spec: {
|
410
|
+
"layer": [
|
411
|
+
{
|
412
|
+
"mark": "bar"
|
413
|
+
},
|
414
|
+
{
|
415
|
+
"mark": {
|
416
|
+
"type": "text",
|
417
|
+
"align": "left",
|
418
|
+
"baseline": "top",
|
419
|
+
"dx": -10,
|
420
|
+
"yOffset": -20
|
421
|
+
},
|
422
|
+
"encoding": {
|
423
|
+
"text": {"field": "Count", "type": "quantitative"}
|
424
|
+
}
|
425
|
+
}
|
426
|
+
],
|
427
|
+
"encoding": {
|
428
|
+
"x": {"field": "Status", "type": "nominal"},
|
429
|
+
"y": {"field": "Count", "type": "quantitative"}
|
430
|
+
}
|
431
|
+
}
|
432
|
+
},
|
433
|
+
{
|
434
|
+
title: "Rails Performance",
|
435
|
+
header: %w[Controller Hits Min Avg Max],
|
436
|
+
column_alignment: %i[left right right right right],
|
437
|
+
rows: data[:performance],
|
438
|
+
vega_spec: {
|
439
|
+
"layer": [
|
440
|
+
{
|
441
|
+
"mark": {
|
442
|
+
"type": "point",
|
443
|
+
"name": "data_points"
|
444
|
+
}
|
445
|
+
},
|
446
|
+
{
|
447
|
+
"mark": {
|
448
|
+
"name": "label",
|
449
|
+
"type": "text",
|
450
|
+
"align": "left",
|
451
|
+
"baseline": "middle",
|
452
|
+
"dx": 5,
|
453
|
+
"yOffset": 0
|
454
|
+
},
|
455
|
+
"encoding": {
|
456
|
+
"text": {"field": "Controller"},
|
457
|
+
"fontSize": {"value": 8}
|
458
|
+
},
|
459
|
+
},
|
460
|
+
],
|
461
|
+
"encoding": {
|
462
|
+
"x": {"field": "Avg", "type": "quantitative"},
|
463
|
+
"y": {"field": "Hits", "type": "quantitative"}
|
464
|
+
},
|
465
|
+
}
|
466
|
+
},
|
467
|
+
{
|
468
|
+
title: "Fatal Events",
|
469
|
+
header: %w[Date IP URL Description Log ID],
|
470
|
+
column_alignment: %i[left left left left left],
|
471
|
+
rows: data[:fatal],
|
472
|
+
col: "small-12 cell"
|
473
|
+
},
|
474
|
+
{
|
475
|
+
title: "Internal Server Errors",
|
476
|
+
header: %w[Date Status IP URL Description Log ID],
|
477
|
+
column_alignment: %i[left left left left left left],
|
478
|
+
rows: data[:internal_server_error],
|
479
|
+
col: "small-12 cell"
|
480
|
+
},
|
481
|
+
{
|
482
|
+
title: "Errors",
|
483
|
+
header: %w[Log ID Context Description Count],
|
484
|
+
column_alignment: %i[left left left left],
|
485
|
+
rows: data[:error],
|
486
|
+
col: "small-12 cell"
|
487
|
+
},
|
488
|
+
{
|
489
|
+
title: "IPs",
|
490
|
+
header: %w[IPs Hits Country],
|
491
|
+
column_alignment: %i[left right left],
|
492
|
+
rows: data[:ips]
|
493
|
+
}
|
494
|
+
]
|
495
|
+
end
|
63
496
|
end
|
64
497
|
end
|
@@ -7,46 +7,46 @@ module LogSense
|
|
7
7
|
#
|
8
8
|
# parse command line options
|
9
9
|
#
|
10
|
-
def self.parse
|
10
|
+
def self.parse(options)
|
11
11
|
limit = 900
|
12
12
|
args = {}
|
13
13
|
|
14
14
|
opt_parser = OptionParser.new do |opts|
|
15
|
-
opts.banner =
|
15
|
+
opts.banner = 'Usage: log_sense [options] [logfile ...]'
|
16
16
|
|
17
|
-
opts.on(
|
17
|
+
opts.on('-tTITLE', '--title=TITLE', String, 'Title to use in the report') do |n|
|
18
18
|
args[:title] = n
|
19
19
|
end
|
20
20
|
|
21
|
-
opts.on(
|
21
|
+
opts.on('-fFORMAT', '--input-format=FORMAT', String, 'Input format (either rails or apache)') do |n|
|
22
22
|
args[:input_format] = n
|
23
23
|
end
|
24
24
|
|
25
|
-
opts.on(
|
26
|
-
args[:input_file] = n
|
27
|
-
end
|
28
|
-
|
29
|
-
opts.on("-tFORMAT", "--output-format=FORMAT", String, "Output format: html, org, txt, sqlite. See below for available formats") do |n|
|
25
|
+
opts.on('-tFORMAT', '--output-format=FORMAT', String, 'Output format: html, org, txt, sqlite. See below for available formats') do |n|
|
30
26
|
args[:output_format] = n
|
31
27
|
end
|
32
28
|
|
33
|
-
opts.on(
|
29
|
+
opts.on('-oOUTPUT_FILE', '--output-file=OUTPUT_FILE', String, 'Output file') do |n|
|
34
30
|
args[:output_file] = n
|
35
31
|
end
|
36
32
|
|
37
|
-
opts.on(
|
33
|
+
opts.on('-bDATE', '--begin=DATE', Date, 'Consider entries after or on DATE') do |n|
|
38
34
|
args[:from_date] = n
|
39
35
|
end
|
40
36
|
|
41
|
-
opts.on(
|
37
|
+
opts.on('-eDATE', '--end=DATE', Date, 'Consider entries before or on DATE') do |n|
|
42
38
|
args[:to_date] = n
|
43
39
|
end
|
44
40
|
|
45
|
-
opts.on(
|
41
|
+
opts.on('-lN', '--limit=N', Integer, "Limit to the N most requested resources (defaults to #{limit})") do |n|
|
46
42
|
args[:limit] = n
|
47
43
|
end
|
48
44
|
|
49
|
-
opts.on(
|
45
|
+
opts.on('-wWIDTH', '--width=WIDTH', Integer, 'Maximum width of URL and description columns in text reports') do |n|
|
46
|
+
args[:width] = n
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on('-cPOLICY', '--crawlers=POLICY', String, 'Decide what to do with crawlers (applies to Apache Logs)') do |n|
|
50
50
|
case n
|
51
51
|
when 'only'
|
52
52
|
args[:only_crawlers] = true
|
@@ -55,30 +55,30 @@ module LogSense
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
opts.on(
|
58
|
+
opts.on('-ns', '--no-selfpoll', 'Ignore self poll entries (requests from ::1; applies to Apache Logs)') do
|
59
59
|
args[:no_selfpoll] = true
|
60
60
|
end
|
61
61
|
|
62
|
-
opts.on(
|
62
|
+
opts.on('-v', '--version', 'Prints version information') do
|
63
63
|
puts "log_sense version #{LogSense::VERSION}"
|
64
|
-
puts
|
65
|
-
puts
|
64
|
+
puts 'Copyright (C) 2021 Shair.Tech'
|
65
|
+
puts 'Distributed under the terms of the MIT license'
|
66
66
|
exit
|
67
67
|
end
|
68
68
|
|
69
|
-
opts.on(
|
69
|
+
opts.on('-h', '--help', 'Prints this help') do
|
70
70
|
puts opts
|
71
|
-
puts
|
71
|
+
puts ''
|
72
72
|
puts "This is version #{LogSense::VERSION}"
|
73
73
|
|
74
|
-
puts
|
75
|
-
puts
|
76
|
-
pathname = File.join(File.dirname(__FILE__),
|
77
|
-
templates = Dir.glob(pathname).select { |x| !
|
78
|
-
components = templates.map { |x| File.basename(x).split
|
74
|
+
puts ''
|
75
|
+
puts 'Output formats'
|
76
|
+
pathname = File.join(File.dirname(__FILE__), 'templates', '*')
|
77
|
+
templates = Dir.glob(pathname).select { |x| !File.basename(x).start_with?(/_|#/) && !File.basename(x).end_with?('~') }
|
78
|
+
components = templates.map { |x| File.basename(x).split '.' }.group_by { |x| x[0] }
|
79
79
|
components.each do |k, vs|
|
80
80
|
puts "#{k} parsing can produce the following outputs:"
|
81
|
-
puts
|
81
|
+
puts ' - sqlite'
|
82
82
|
vs.each do |v|
|
83
83
|
puts " - #{v[1]}"
|
84
84
|
end
|
@@ -91,13 +91,13 @@ module LogSense
|
|
91
91
|
opt_parser.parse!(options)
|
92
92
|
|
93
93
|
args[:limit] ||= limit
|
94
|
-
args[:input_format] ||=
|
95
|
-
args[:output_format] ||=
|
94
|
+
args[:input_format] ||= 'apache'
|
95
|
+
args[:output_format] ||= 'html'
|
96
96
|
args[:ignore_crawlers] ||= false
|
97
97
|
args[:only_crawlers] ||= false
|
98
98
|
args[:no_selfpoll] ||= false
|
99
99
|
|
100
|
-
|
100
|
+
args
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
@@ -19,9 +19,10 @@ module LogSense
|
|
19
19
|
@last_day = last_day_s&.first&.first ? Date.parse(last_day_s[0][0]) : nil
|
20
20
|
|
21
21
|
@total_days = 0
|
22
|
-
if @first_day
|
23
|
-
|
24
|
-
|
22
|
+
@total_days = (@last_day - @first_day).to_i if @first_day && @last_day
|
23
|
+
|
24
|
+
# TODO should also look into Error
|
25
|
+
@source_files = db.execute "SELECT distinct(source_file) from Event"
|
25
26
|
|
26
27
|
@log_size = db.execute "SELECT count(started_at) from Event"
|
27
28
|
@log_size = @log_size[0][0]
|