apache_log_report 0.9.7 → 1.1.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.
@@ -1,521 +1,5 @@
1
- module ApacheLogReport
2
-
3
- #
4
- # parse command line options
5
- #
6
- require 'optparse'
7
- require 'optparse/date'
8
- require 'apache_log_report/version'
9
-
10
- def self.options_parse options
11
- limit = 30
12
- args = {}
13
-
14
- opt_parser = OptionParser.new do |opts|
15
- opts.banner = "Usage: apache_log_report [options] [logfile]"
16
-
17
- opts.on("-lN", "--limit=N", Integer, "Number of entries to show (defaults to #{limit})") do |n|
18
- args[:limit] = n
19
- end
20
-
21
- opts.on("-bDATE", "--from-date=DATE", DateTime, "Consider entries after or on DATE") do |n|
22
- args[:from_date] = n
23
- end
24
-
25
- opts.on("-eDATE", "--to-date=DATE", DateTime, "Consider entries before or on DATE") do |n|
26
- args[:to_date] = n
27
- end
28
-
29
- opts.on("-i", "--ignore-crawlers", "Ignore crawlers") do
30
- args[:ignore_crawlers] = true
31
- end
32
-
33
- opts.on("-p", "--ignore-selfpoll", "Ignore apaches self poll entries (from ::1)") do
34
- args[:no_selfpoll] = true
35
- end
36
-
37
- opts.on("-c", "--only-crawlers", "Perform analysis on crawlers only") do
38
- args[:only_crawlers] = true
39
- end
40
-
41
- opts.on("-uPREFIX", "--prefix=PREFIX", String, "Prefix to add to all plots (used to run multiple analyses in the same dir)") do |n|
42
- args[:prefix] = n
43
- end
44
-
45
- opts.on("-wSUFFIX", "--suffix=SUFFIX", String, "Suffix to add to all plots (used to run multiple analyses in the same dir)") do |n|
46
- args[:suffix] = n
47
- end
48
-
49
- opts.on("-cWHAT", "--code-export=WHAT", String, "Control :export directive in code blocks (code, results, *both*, none)") do |n|
50
- args[:code_export] = n
51
- end
52
-
53
- opts.on("-v", "--version", "Prints version information") do
54
- puts "apache_log_report version #{ApacheLogReport::VERSION}"
55
- puts "Copyright (C) 2020 Adolfo Villafiorita"
56
- puts "Distributed under the terms of the MIT license"
57
- puts ""
58
- puts "Written by Adolfo Villafiorita"
59
- exit
60
- end
61
-
62
- opts.on("-h", "--help", "Prints this help") do
63
- puts opts
64
- puts "This is version #{ApacheLogReport::VERSION}"
65
- exit
66
- end
67
- end
68
-
69
- opt_parser.parse!(options)
70
-
71
- args[:limit] ||= limit
72
- args[:ignore_crawlers] ||= false
73
- args[:no_selfpoll] ||= false
74
- args[:only_crawlers] ||= false
75
- args[:prefix] ||= ""
76
- args[:suffix] ||= ""
77
- args[:code_export] ||= "both"
78
-
79
- return args
80
- end
81
-
82
-
83
-
84
- #
85
- # parse an Apache log file and return a SQLite3 DB
86
- #
87
- require 'apache_log/parser'
88
- require 'sqlite3'
89
- require 'browser'
90
-
91
- def self.parse filename, options = {}
92
- content = filename ? File.readlines(filename) : ARGF.readlines
93
-
94
- db = SQLite3::Database.new ":memory:"
95
- db.execute "CREATE TABLE IF NOT EXISTS LogLine(
96
- id INTEGER PRIMARY KEY AUTOINCREMENT,
97
- datetime TEXT,
98
- ip TEXT,
99
- user TEXT,
100
- unique_visitor TEXT,
101
- method TEXT,
102
- path TEXT,
103
- extension TEXT,
104
- status TEXT,
105
- size INTEGER,
106
- referer TEXT,
107
- user_agent TEXT,
108
- bot INTEGER,
109
- browser TEXT,
110
- browser_version TEXT,
111
- platform TEXT,
112
- platform_version TEXT)"
113
-
114
- ins = db.prepare('insert into LogLine (
115
- datetime,
116
- ip,
117
- user,
118
- unique_visitor,
119
- method,
120
- path,
121
- extension,
122
- status,
123
- size,
124
- referer,
125
- user_agent,
126
- bot,
127
- browser,
128
- browser_version,
129
- platform,
130
- platform_version)
131
- values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
132
-
133
- parser = ApacheLog::Parser.new(options[:format] || 'combined')
134
-
135
- content.each do |line|
136
- begin
137
- hash = parser.parse line
138
-
139
- ua = Browser.new(hash[:user_agent], accept_language: "en-us")
140
- ins.execute(
141
- hash[:datetime].iso8601,
142
- hash[:remote_host],
143
- hash[:user],
144
- hash[:datetime].iso8601 + " " + hash[:remote_host] + " " + hash[:user_agent],
145
- hash[:request][:method],
146
- hash[:request][:path],
147
- (hash[:request][:path] ? File.extname(hash[:request][:path]) : ""),
148
- hash[:status],
149
- hash[:size].to_i,
150
- hash[:referer],
151
- hash[:user_agent],
152
- ua.bot? ? 1 : 0,
153
- (ua.name || ""),
154
- (ua.version || ""),
155
- (ua.platform.name || ""),
156
- (ua.platform.version || "")
157
- )
158
- rescue
159
- STDERR.puts "Apache Log parser error: could not parse #{line}"
160
- end
161
- end
162
-
163
- db
164
- end
165
-
166
- #
167
- # take a sqlite3 databae and analyze data
168
- #
169
- def self.analyze_data db, options = {}
170
-
171
- @first_day = db.execute "SELECT datetime from LogLine order by datetime limit 1"
172
- @last_day = db.execute "SELECT datetime from LogLine order by datetime desc limit 1"
173
- @log_size = db.execute "SELECT count(datetime) from LogLine"
174
- @crawlers_size = db.execute "SELECT count(datetime) from LogLine where bot == 1"
175
- @selfpolls_size = db.execute "SELECT count(datetime) from LogLine where ip == '::1'"
176
-
177
- #
178
- # generate the where clause corresponding to the command line options to filter data
179
- #
180
- @filter = [
181
- (options[:from_date] ? "date(datetime) >= '#{options[:from_date]}'" : nil),
182
- (options[:to_date] ? "date(datetime) <= '#{options[:to_date]}'" : nil),
183
- (options[:only_crawlers] ? "bot == 1" : nil),
184
- (options[:ignore_crawlers] ? "bot == 0" : nil),
185
- (options[:no_selfpolls] ? "ip != '::1'" : nil),
186
- "true"
187
- ].compact.join " and "
188
-
189
- @total_hits = db.execute "SELECT count(datetime) from LogLine where #{@filter}"
190
- @total_unique_visitors = db.execute "SELECT count(distinct(unique_visitor)) from LogLine where #{@filter}"
191
- @total_size = db.execute "SELECT sum(size) from LogLine where #{@filter}"
192
- @total_days = (Date.parse(@last_day[0][0]) - Date.parse(@first_day[0][0])).to_i
193
-
194
- @daily_distribution = db.execute "SELECT date(datetime), count(datetime), count(distinct(unique_visitor)), sum(size) from LogLine where #{@filter} group by date(datetime)"
195
-
196
- @time_distribution = db.execute "SELECT strftime('%H', datetime), count(datetime), count(distinct(unique_visitor)), sum(size) from LogLine where #{@filter} group by strftime('%H', datetime)"
197
-
198
- @most_requested_pages = db.execute "SELECT path, count(path), count(distinct(unique_visitor)), sum(size) from LogLine where extension == '.html' and #{@filter} group by path order by count(path) desc limit #{options[:limit]}"
199
-
200
- @most_requested_resources = db.execute "SELECT path, count(path), count(distinct(unique_visitor)), sum(size) from LogLine where #{@filter} group by path order by count(path) desc limit #{options[:limit]}"
201
-
202
- @missed_pages = db.execute "SELECT path, count(path), count(distinct(unique_visitor)) from LogLine where status == '404' and extension == '.html' and #{@filter} group by path order by count(path) desc limit #{options[:limit]}"
203
-
204
- @missed_resources = db.execute "SELECT path, count(path), count(distinct(unique_visitor)) from LogLine where status == '404' and #{@filter} group by path order by count(path) desc limit #{options[:limit]}"
205
-
206
- @reasonable_requests_exts = [ ".html", ".css", ".js", ".jpg", ".svg", ".png", ".woff", ".xml", ".ttf", ".ico", ".pdf", ".htm", ".txt", ".org" ].map { |x|
207
- "extension != '#{x}'"
208
- }.join " and "
209
-
210
- @attacks = db.execute "SELECT path, count(path), count(distinct(unique_visitor)) from LogLine where status == '404' and #{@filter} and (#{@reasonable_requests_exts}) group by path order by count(path) desc limit #{options[:limit]}"
211
-
212
- @statuses = db.execute "SELECT status, count(status) from LogLine where #{@filter} group by status order by status"
213
-
214
- @by_day_4xx = db.execute "SELECT date(datetime), count(datetime) from LogLine where substr(status, 1,1) == '4' and #{@filter} group by date(datetime)"
215
- @by_day_3xx = db.execute "SELECT date(datetime), count(datetime) from LogLine where substr(status, 1,1) == '3' and #{@filter} group by date(datetime)"
216
- @by_day_2xx = db.execute "SELECT date(datetime), count(datetime) from LogLine where substr(status, 1,1) == '2' and #{@filter} group by date(datetime)"
217
-
218
- @statuses_by_day = (@by_day_2xx + @by_day_3xx + @by_day_4xx).group_by { |x| x[0] }.to_a.map { |x|
219
- [x[0], x[1].map { |y| y[1] }].flatten
220
- }
221
-
222
- @browsers = db.execute "SELECT browser, count(browser), count(distinct(unique_visitor)), sum(size) from LogLine where #{@filter} group by browser order by count(browser) desc"
223
-
224
- @platforms = db.execute "SELECT platform, count(platform), count(distinct(unique_visitor)), sum(size) from LogLine where #{@filter} group by platform order by count(platform) desc"
225
-
226
- @ips = db.execute "SELECT ip, count(ip), count(distinct(unique_visitor)), sum(size) from LogLine where #{@filter} group by ip order by count(ip) desc limit #{options[:limit]}"
227
-
228
- @referers = db.execute "SELECT referer, count(referer), count(distinct(unique_visitor)), sum(size) from LogLine where #{@filter} group by referer order by count(referer) desc limit #{options[:limit]}"
229
- end
230
-
231
-
232
- #
233
- # Emit Data
234
- #
235
-
236
- require 'terminal-table'
237
-
238
- def self.output_table name, headings, rows
239
- name = "#+NAME: #{name}"
240
- table = Terminal::Table.new headings: headings, rows: rows, style: { border_x: "-", border_i: "|" }
241
-
242
- #(2..headings.size).each do |i|
243
- # table.align_column(i, :right)
244
- #end
245
-
246
- name + "\n" + table.to_s
247
- end
248
-
249
- def self.emit options = {}, command, log_file, started_at, ended_at, duration
250
- @prefix = options[:prefix]
251
- @suffix = options[:suffix]
252
- @export = options[:code_export]
253
-
254
- <<EOS
255
- #+TITLE: Apache Log Analysis: #{log_file}
256
- #+DATE: <#{Date.today}>
257
- #+STARTUP: showall
258
- #+OPTIONS: ^:{}
259
- #+HTML_HEAD: <link rel="stylesheet" type="text/css" href="ala-style.css" />
260
- #+OPTIONS: html-style:nil
261
-
262
- * Summary
263
-
264
- | Hits | #{"%10d" % @total_hits[0][0]} |
265
- | Unique Visitors | #{"%10d" % @total_unique_visitors[0][0] } |
266
- | Tx | #{"%10d" % @total_size[0][0] } |
267
- | Days | #{"%10d" % @total_days[0][0] } |
268
-
269
- * Daily Distribution
270
-
271
- #{ output_table "daily_distribution", ["Day", "Hits", "Visits", "Size"], @daily_distribution }
272
-
273
- #+BEGIN_SRC gnuplot :var data = daily_distribution :results output :exports #{@export} :file #{@prefix}daily#{@suffix}.svg
274
- reset
275
- set grid ytics linestyle 0
276
- set grid xtics linestyle 0
277
- set terminal svg size 1200,800 fname 'Arial'
278
-
279
- set xdata time
280
- set timefmt "%Y-%m-%d"
281
- set format x "%a, %b %d"
282
- set xtics rotate by 60 right
283
-
284
- set title "Hits and Visitors"
285
- set xlabel "Date"
286
- set ylabel "Hits"
287
- set y2label "Visits"
288
- set y2tics
289
-
290
- set style fill transparent solid 0.2 noborder
291
-
292
- plot data using 1:2 with linespoints lw 3 lc rgb "#0000AA" pointtype 5 title "Hits" axes x1y2, \\
293
- data using 1:2 with filledcurves below x1 linecolor rgb "#0000AA" notitle axes x1y2, \\
294
- data using 1:3 with linespoints lw 3 lc rgb "#AA0000" pointtype 7 title "Visitors", \\
295
- data using 1:3 with filledcurves below x1 notitle linecolor rgb "#AA0000", \\
296
- data using 1:($3+10):3 with labels notitle textcolor rgb "#AA0000", \\
297
- data using 1:($2+100):2 with labels notitle textcolor rgb "#0000AA" axes x1y2
298
- #+END_SRC
299
-
300
-
301
- * Time Distribution
302
-
303
- #{ output_table "time_distribution", ["Hour", "Hits", "Visits", "Size"], @time_distribution }
304
-
305
-
306
- #+BEGIN_SRC gnuplot :var data = time_distribution :results output :exports #{@export} :file #{@prefix}time#{@suffix}.svg
307
- reset
308
- set terminal svg size 1200,800 fname 'Arial' fsize 10
309
-
310
- set grid ytics linestyle 0
311
-
312
- set title "Hits and Visitors"
313
- set xlabel "Date"
314
- set ylabel "Hits"
315
- set y2label "Visitors"
316
- set y2tics
317
-
318
- set style fill solid 0.25
319
- set boxwidth 0.6
320
-
321
- set style data histograms
322
- set style histogram clustered gap 1
323
-
324
- plot data using 2:xtic(1) lc rgb "#0000AA" title "Hits", \\
325
- data using 3 lc rgb "#AA0000" title "Visitors" axes x1y2, \\
326
- data using ($0 - 0.2):($2 + 10):2 with labels title "" textcolor rgb("#0000AA"), \\
327
- data using ($0 + 0.2):($3 + 10):3 with labels title "" textcolor rgb("#AA0000") axes x1y2
328
- #+END_SRC
329
-
330
- #+BEGIN_SRC gnuplot :var data = time_distribution :results output :exports #{@export} :file #{@prefix}time-traffic#{@suffix}.svg
331
- reset
332
- set terminal svg size 1200,800 fname 'Arial' fsize 10
333
-
334
- set grid ytics linestyle 0
335
-
336
- set title "Traffic"
337
- set xlabel "Date"
338
- set ylabel "Traffic"
339
-
340
- set style fill solid 0.50
341
- set boxwidth 0.6
342
-
343
- set style data histograms
344
- set style histogram clustered gap 1
345
-
346
- plot data using 2:xtic(1) lc rgb "#00AA00" title "Traffic", \\
347
- data using ($0):($2 + 10):2 with labels title "" textcolor rgb("#00AA00")
348
- #+END_SRC
349
-
350
- * Most Requested Pages
351
-
352
- #{ output_table "most_requested_pages", ["Path", "Hits", "Visits", "Size"], @most_requested_pages }
353
-
354
- * Most Requested URIs
355
-
356
- #{ output_table "most_requested_resources", ["Path", "Hits", "Visits", "Size"], @most_requested_resources }
357
-
358
- * 404s on HTML files
359
-
360
- #{ output_table "pages_404", ["Path", "Hits", "Visitors"], @missed_pages }
361
-
362
- * 404s on other resources
363
-
364
- #{ output_table "resources_404", ["Path", "Hits", "Visitors"], @missed_resources }
365
-
366
- * Possible Attacks
367
-
368
- #{ output_table "attacks", ["Path", "Hits", "Visitors"], @attacks }
369
-
370
- * Statuses
371
-
372
- #{ output_table "statuses", ["Status", "Count"], @statuses }
373
-
374
- #+BEGIN_SRC gnuplot :var data = statuses :results output :exports #{@export} :file #{@prefix}statuses#{@suffix}.svg
375
- reset
376
- set grid ytics linestyle 0
377
- set terminal svg size 1200,800 fname 'Arial' fsize 10
378
-
379
- set style fill solid 0.25
380
- set boxwidth 0.6
381
-
382
- plot data using 2:xtic(1) with boxes lc rgb "#0000AA" title "Hits", \\
383
- data using ($0):($2+100):2 with labels textcolor rgb "#0000AA"
384
- #+END_SRC
385
-
386
- * Daily Statuses
387
-
388
- #{ output_table "daily_statuses", ["Status", "2xx", "3xx", "4xx"], @statuses_by_day }
389
-
390
- #+BEGIN_SRC gnuplot :var data = daily_statuses :results output :exports #{@export} :file #{@prefix}daily-statuses#{@suffix}.svg
391
- reset
392
- set terminal svg size 1200,800 fname 'Arial' fsize 10
393
-
394
- set grid ytics linestyle 0
395
-
396
- set title "Daily Statuses"
397
- set xlabel "Date"
398
- set ylabel "Number of Hits"
399
- set xtics rotate by 60 right
400
-
401
- set style fill solid 0.25
402
- set boxwidth 0.6
403
-
404
- set style data histograms
405
- set style histogram clustered gap 1
406
-
407
- plot data using 2:xtic(1) lc rgb "#00AA00" title "2xx", \\
408
- data using 3 lc rgb "#0000CC" title "3xx", \\
409
- data using 4 lc rgb "#AA0000" title "4xx", \\
410
- data using ($0 - 1. / 4):($2 + 0.5):2 with labels title "" textcolor rgb("#00AA00"), \\
411
- data using ($0):($3 + 0.5):3 with labels title "" textcolor rgb("#0000CC"), \\
412
- data using ($0 + 1. / 4):($4 + 0.5):4 with labels title "" textcolor rgb("#AA0000")
413
- #+END_SRC
414
-
415
- * Browsers
416
-
417
- #{ output_table "browsers", ["Browser", "Hits", "Visitors", "Size"], @browsers }
418
-
419
- #+BEGIN_SRC gnuplot :var data = browsers :results output :exports #{@export} :file #{@prefix}browser#{@suffix}.svg
420
- reset
421
- set grid ytics linestyle 0
422
- set terminal svg size 1200,800 fname 'Arial' fsize 10
423
-
424
- set style fill solid 0.25
425
- set boxwidth 0.6
426
-
427
- plot data using 2:xtic(1) with boxes lc rgb "#0000AA" title "Hits", \\
428
- data using ($0):($2+100):2 with labels textcolor rgb "#0000AA"
429
- #+END_SRC
430
-
431
- * Platforms
432
-
433
- #{ output_table "platforms", ["Platform", "Hits", "Visitors", "Size"], @platforms }
434
-
435
- #+BEGIN_SRC gnuplot :var data = platforms :results output :exports #{@export} :file #{@prefix}platforms#{@suffix}.svg
436
- reset
437
- set grid ytics linestyle 0
438
- set terminal svg size 1200,800 fname 'Arial' fsize 10
439
-
440
- set style fill solid 0.25
441
- set boxwidth 0.6
442
-
443
- plot data using 2:xtic(1) with boxes lc rgb "#0000AA" title "Hits", \\
444
- data using ($0):($2+100):2 with labels textcolor rgb "#0000AA"
445
- #+END_SRC
446
-
447
- * IPs
448
-
449
- #{ output_table "ips", ["IPs", "Hits", "Visitors", "Size"], @ips }
450
-
451
-
452
- * Referers
453
-
454
- #{ output_table "referers", ["Referers", "Hits", "Visitors", "Size"], @referers }
455
-
456
- #+BEGIN_SRC gnuplot :var data = referers :results output :exports #{@export} :file #{@prefix}referers#{@suffix}.svg
457
- reset
458
- set terminal svg size 1200,800 fname 'Arial' fsize 10
459
-
460
- set grid ytics linestyle 0
461
- set grid xtics linestyle 0
462
-
463
- set title "Referers"
464
- set xlabel "Date"
465
- set xtics rotate by 60 right
466
- set ylabel "Hits and Visits"
467
-
468
- set style fill solid 0.45
469
- set boxwidth 0.7
470
-
471
- set style data histograms
472
- set style histogram clustered gap 1
473
-
474
- plot data using 2:xtic(1) lc rgb "#AA00AA" title "Hits", \\
475
- data using 3 lc rgb "#0AAAA0" title "Visits", \\
476
- data using ($0 - 1. / 3):($2 + 50):2 with labels title "" textcolor rgb("#AA00AA"), \\
477
- data using ($0 + 1. / 3):($3 + 50):3 with labels title "" textcolor rgb("#0AAAA0")
478
- #+END_SRC
479
-
480
- * Command Invocation and Performance
481
-
482
- ** Command Invocation
483
-
484
- #+BEGIN_EXAMPLE shell
485
- #{command}
486
- #+END_EXAMPLE
487
-
488
- | Input file | #{"%-50s" % (log_file || "stdin")} |
489
- | Ignore crawlers | #{"%-50s" % options[:ignore_crawlers]} |
490
- | Only crawlers | #{"%-50s" % options[:only_crawlers]} |
491
- | No selfpoll | #{"%-50s" % options[:no_selfpoll]} |
492
- | Filter by date | #{"%-50s" % (options[:from_date] != nil or options[:to_date] != nil)} |
493
- | Prefix | #{"%-50s" % @prefix} |
494
- | Suffix | #{"%-50s" % @suffix} |
495
-
496
- ** Log Structure
497
-
498
- | Log size | #{"%10d" % @log_size[0][0]} |
499
- | Self poll entries | #{"%10d" % @selfpolls_size[0][0]} |
500
- | Crawlers | #{"%10d" % @crawlers_size[0][0]} |
501
- | Entries considered | #{"%10d" % @total_hits[0][0]} |
502
-
503
- ** Performance
504
-
505
- | Analysis started at | #{started_at.to_s} |
506
- | Analysis ended at | #{ended_at.to_s} |
507
- | Duration (sec) | #{"%5.3d" % duration } |
508
- | Duration (min) | #{"%5.3d" % (duration / 60 )} |
509
- | Log size | #{"%9d" % @log_size[0][0]} |
510
- | Lines/sec | #{"%6.2f" % (@log_size[0][0] / duration)} |
511
-
512
- * Local Variables :noexport:
513
- # Local Variables:
514
- # org-confirm-babel-evaluate: nil
515
- # org-display-inline-images: t
516
- # end:
517
- EOS
518
- end
519
- end
520
-
521
-
1
+ require 'apache_log_report/version'
2
+ require 'apache_log_report/options_parser'
3
+ require 'apache_log_report/log_parser'
4
+ require 'apache_log_report/data_cruncher'
5
+ require 'apache_log_report/emitter'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apache_log_report
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.7
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adolfo Villafiorita
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-29 00:00:00.000000000 Z
11
+ date: 2021-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: apache_log-parser
@@ -77,6 +77,7 @@ files:
77
77
  - ".gitignore"
78
78
  - CHANGELOG.org
79
79
  - Gemfile
80
+ - Gemfile.lock
80
81
  - LICENSE.txt
81
82
  - README.org
82
83
  - Rakefile
@@ -85,6 +86,14 @@ files:
85
86
  - bin/setup
86
87
  - exe/apache_log_report
87
88
  - lib/apache_log_report.rb
89
+ - lib/apache_log_report/apache_log_report.rb
90
+ - lib/apache_log_report/data_cruncher.rb
91
+ - lib/apache_log_report/emitter.rb
92
+ - lib/apache_log_report/log_parser.rb
93
+ - lib/apache_log_report/options_parser.rb
94
+ - lib/apache_log_report/templates/_output_table.html.erb
95
+ - lib/apache_log_report/templates/template.html.erb
96
+ - lib/apache_log_report/templates/template.org.erb
88
97
  - lib/apache_log_report/version.rb
89
98
  homepage: https://www.ict4g.net/gitea/adolfo/apache_log_report
90
99
  licenses:
@@ -109,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
118
  - !ruby/object:Gem::Version
110
119
  version: '0'
111
120
  requirements: []
112
- rubygems_version: 3.0.3
121
+ rubygems_version: 3.2.22
113
122
  signing_key:
114
123
  specification_version: 4
115
124
  summary: Generate a request report in OrgMode format from an Apache log file.