apache_log_report 1.1.4 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b90448ec269bd64d60c2323888fb77ea8ac7307a00198434119a6f0da605585
4
- data.tar.gz: 24569c78964b795478c50e5de01c4f42fb2be8f02c30911e4e81229a96917a55
3
+ metadata.gz: d957c3ba44653ff3812aa0ad5f912edae8691f32c900cee509a005748319de81
4
+ data.tar.gz: 792424f5272630630ea896599aa0f814f72dc1ff599cb0f238327d17a603044f
5
5
  SHA512:
6
- metadata.gz: 604697690824080fbbed523f26c1727f969fcf791803ebd15d6838384c5c77edbcde25ad74fa47c2371c0690019745948b6d78f9f1a51e6742e51e35c5c5ba76
7
- data.tar.gz: b7d5a6940308a6a098cb61b9118cd17760592e299e091193b07a003ae87e6f7058ee1aab81aed2cadd65dcc07c6b64652ede126274be5be552dbbf0ca3ba11b1
6
+ metadata.gz: 0b649cf5abc5e186372f853f2f127ea3afd6eb514f13ee2a006bf9310c4acc2e92f8fdd22414609fdd13a1197c377b3ccc9fde73d6cf95d6c9930056e90409aa
7
+ data.tar.gz: 7085f94df8b7029fd1fb8c011a9b187e0490f86501abacf8d205c608eb554a96abe70bc792ead20e3d5fc4f8b27572209f5a28f4b2ea2d0bb4c74498ef93e768
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- apache_log_report (1.1.2)
4
+ apache_log_report (1.1.7)
5
5
  apache_log-parser
6
6
  browser
7
7
  sqlite3
@@ -14,9 +14,9 @@ GEM
14
14
  browser (5.3.1)
15
15
  rake (12.3.3)
16
16
  sqlite3 (1.4.2)
17
- terminal-table (3.0.2)
18
- unicode-display_width (>= 1.1.1, < 3)
19
- unicode-display_width (2.1.0)
17
+ terminal-table (2.0.0)
18
+ unicode-display_width (~> 1.1, >= 1.1.1)
19
+ unicode-display_width (1.7.0)
20
20
 
21
21
  PLATFORMS
22
22
  ruby
data/README.org CHANGED
@@ -4,21 +4,102 @@
4
4
 
5
5
  * Introduction
6
6
 
7
+ ApacheLogReport generates reports and statistics from Apache web logs
8
+ in the =combined= format. Written in Ruby, it runs from the command
9
+ line, it is fast, and it can be installed on any system which supports
10
+ Ruby.
11
+
12
+ ApacheLogReport moves along the lines of tools such as [[https://goaccess.io/][GoAccess]]
13
+ and [[https://umami.is/][Umami]], focusing on privacy and data-ownership: the data
14
+ generated by ApacheLogReport is stored on your computer and owned by
15
+ you (like it should be).
16
+
17
+ ApacheLogReport is also inspired by static websites generators:
18
+ statistics are generated from the command line and accessed as static
19
+ HTML files. By generating static resources, ApacheLogReport
20
+ significantly reduces the attack surface of your webserver and
21
+ installation headaches.
22
+
23
+ We have, for instance, a cron job running on our servers, generating
24
+ statistics at night. The generated files are then made available on a
25
+ private area on the web.
26
+
27
+ Statistics are generated from Apache log formats in the =combined=
28
+ format. Reports are tailored, but not limited, to web servers serving
29
+ static websites. No need to install Java Script code on your
30
+ websites, no cookies installed, no user tracking.
31
+
32
+ ApacheLogReport reports the following data:
33
+
34
+ - Visitors, hits, unique visitors, bandwidth used
35
+ - Most accessed HTML pages
36
+ - Most accessed resources
37
+ - Response statuses
38
+ - referers
39
+ - OS, browsers, and devices
40
+ - Streaks: resources accessed by a given IP over time
41
+ - Potential attacks: access to resources which are not meant to be
42
+ served by a web server serving static websites
43
+
44
+ Filters from the command line allow to analyze specific periods and
45
+ distinguish traffic generated by self polls and crawlers.
46
+
47
+ ApacheLogReport generates HTML and SQLite outputs. Moreover, it can
48
+ also generate reports in Org Mode format, which can be then processed
49
+ to various formats (including LaTeX and HTML).
50
+
7
51
  * Installation
8
52
 
53
+ #+begin_src bash
54
+ gem install apache_log_report
55
+ #+end_src
56
+
9
57
  * Usage
10
58
 
59
+ #+begin_src bash :results raw output :wrap example
60
+ apache_log_report --help
61
+ #+end_src
62
+
63
+ #+RESULTS:
64
+ #+begin_example
65
+ Usage: apache_log_report [options] [logfile]
66
+ -l, --limit=N Number of entries to show (defaults to 30)
67
+ -b, --begin=DATE Consider entries after or on DATE
68
+ -e, --end=DATE Consider entries before or on DATE
69
+ -i, --ignore-crawlers Ignore crawlers
70
+ -p, --ignore-selfpoll Ignore apaches self poll entries (from ::1)
71
+ --only-crawlers Perform analysis on crawlers only
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
76
+ -v, --version Prints version information
77
+ -h, --help Prints this help
78
+ This is version 1.1.6
79
+ #+end_example
80
+
11
81
  * Change Log
12
82
 
13
83
  See the [[file:CHANGELOG.org][CHANGELOG]] file.
14
84
 
15
85
  * Todo
16
86
 
87
+ ** TODO Referers should only include the hostname?
17
88
  ** TODO Graphs in HTML output
18
- ** TODO Countries
89
+ ** TODO Countries/IP Lookup
90
+ ** TODO Light and Dark Theme
91
+ ** TODO Embed CSS
92
+ ** TODO Declare datatypes in table outputs, so that we can format data
19
93
 
20
94
  * Compatibility
21
95
 
96
+ ApacheLogReport should run on any system on which Ruby runs.
97
+
98
+ Concerning the outputs:
99
+
100
+ - The HTML report uses [[https://picturepan2.github.io/spectre/][Spectre.css]] and (will use) [[https://vega.github.io/vega-lite/][Vega Light]], which
101
+ are downloaded from a CDN
102
+ - Org Mode plots data using [[http://gnuplot.info/][Gnuplot]]
22
103
 
23
104
  * Author and Contributors
24
105
 
@@ -1,17 +1,41 @@
1
+
1
2
  module ApacheLogReport
2
3
  module DataCruncher
3
4
 
4
5
  #
5
6
  # take a sqlite3 database and analyze data
6
7
  #
8
+ # @ variables are automatically put in the returned data
9
+ #
7
10
 
8
11
  def self.crunch db, options = {}
9
- @first_day = db.execute "SELECT datetime from LogLine order by datetime limit 1"
10
- @last_day = db.execute "SELECT datetime from LogLine order by datetime desc limit 1"
12
+ first_day_s = db.execute "SELECT datetime from LogLine order by datetime limit 1"
13
+ last_day_s = db.execute "SELECT datetime from LogLine order by datetime desc limit 1"
14
+
15
+ # make first and last day into dates or nil
16
+ @first_day = first_day_s.empty? ? nil : Date.parse(first_day_s[0][0])
17
+ @last_day = last_day_s.empty? ? nil : Date.parse(last_day_s[0][0])
18
+
19
+ @total_days = 0
20
+ if @first_day and @last_day
21
+ @total_days = (@last_day - @first_day).to_i
22
+ end
23
+
11
24
  @log_size = db.execute "SELECT count(datetime) from LogLine"
12
25
  @crawlers_size = db.execute "SELECT count(datetime) from LogLine where bot == 1"
13
26
  @selfpolls_size = db.execute "SELECT count(datetime) from LogLine where ip == '::1'"
14
27
 
28
+ @first_day_requested = options[:from_date]
29
+ @last_day_requested = options[:to_date]
30
+
31
+ @first_day_in_analysis = date_intersect options[:from_date], @first_day, :max
32
+ @last_day_in_analysis = date_intersect options[:to_date], @last_day, :min
33
+
34
+ @total_days_in_analysis = 0
35
+ if @first_day_in_analysis and @last_day_in_analysis
36
+ @total_days_in_analysis = (@last_day_in_analysis - @first_day_in_analysis).to_i
37
+ end
38
+
15
39
  #
16
40
  # generate the where clause corresponding to the command line options to filter data
17
41
  #
@@ -39,12 +63,23 @@ module ApacheLogReport
39
63
  END AS size
40
64
  EOS
41
65
 
66
+ human_readable_day = <<-EOS
67
+ case cast (strftime('%w', datetime) as integer)
68
+ when 0 then 'Sunday'
69
+ when 1 then 'Monday'
70
+ when 2 then 'Tuesday'
71
+ when 3 then 'Wednesday'
72
+ when 4 then 'Thursday'
73
+ when 5 then 'Friday'
74
+ else 'Saturday'
75
+ end as dow
76
+ EOS
77
+
42
78
  @total_hits = db.execute "SELECT count(datetime) from LogLine where #{filter}"
43
79
  @total_unique_visitors = db.execute "SELECT count(distinct(unique_visitor)) from LogLine where #{filter}"
44
80
  @total_size = db.execute "SELECT #{human_readable_size} from LogLine where #{filter}"
45
- @total_days = (Date.parse(@last_day[0][0]) - Date.parse(@first_day[0][0])).to_i
46
81
 
47
- @daily_distribution = db.execute "SELECT date(datetime), count(datetime), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where #{filter} group by date(datetime)"
82
+ @daily_distribution = db.execute "SELECT date(datetime), #{human_readable_day}, count(datetime), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where #{filter} group by date(datetime)"
48
83
  @time_distribution = db.execute "SELECT strftime('%H', datetime), count(datetime), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where #{filter} group by strftime('%H', datetime)"
49
84
  @most_requested_pages = db.execute "SELECT path, count(path), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where extension == '.html' and #{filter} group by path order by count(path) desc limit #{options[:limit]}"
50
85
  @most_requested_resources = db.execute "SELECT path, count(path), count(distinct(unique_visitor)), #{human_readable_size} from LogLine where #{filter} group by path order by count(path) desc limit #{options[:limit]}"
@@ -80,7 +115,20 @@ module ApacheLogReport
80
115
  end
81
116
  data
82
117
  end
83
- end
84
118
 
119
+ private
120
+
121
+ def self.date_intersect date1, date2, method
122
+ if date1 and date2
123
+ [date1, date2].send(method)
124
+ elsif date1
125
+ date1
126
+ else
127
+ date2
128
+ end
129
+ end
130
+
131
+
132
+ end
85
133
  end
86
134
 
@@ -18,11 +18,11 @@ module ApacheLogReport
18
18
  args[:limit] = n
19
19
  end
20
20
 
21
- opts.on("-bDATE", "--begin=DATE", DateTime, "Consider entries after or on DATE") do |n|
21
+ opts.on("-bDATE", "--begin=DATE", Date, "Consider entries after or on DATE") do |n|
22
22
  args[:from_date] = n
23
23
  end
24
24
 
25
- opts.on("-eDATE", "--end=DATE", DateTime, "Consider entries before or on DATE") do |n|
25
+ opts.on("-eDATE", "--end=DATE", Date, "Consider entries before or on DATE") do |n|
26
26
  args[:to_date] = n
27
27
  end
28
28
 
@@ -10,45 +10,72 @@
10
10
  <link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-icons.min.css">
11
11
  </head>
12
12
 
13
- <body class="container">
14
- <h1>Apache Log Analysis: <%= data[:log_file] || "stdin" %></h1>
15
-
16
- <nav>
17
- <ul class="nav">
18
- <li class="nav-item active">
19
- <a href="#">Navigation</a>
20
- <ul class="nav">
21
- <% [ "Summary",
22
- "Log Structure",
23
- "Daily Distribution",
24
- "Time Distribution",
25
- "Most Requested Pages",
26
- "Most Requested Resources",
27
- "404 on HTML Files",
28
- "404 on other Resources",
29
- "Attacks",
30
- "Statuses",
31
- "Daily Statuses",
32
- "Browsers",
33
- "Platforms",
34
- "Referers",
35
- "IPs"
36
- ].each do |item| %>
37
- <li class="nav-item">
38
- <a href="#<%= item.downcase.gsub(' ', '-') %>"><%= item %></a>
39
- </li>
40
- <% end %>
41
- </ul>
42
- </li>
43
- </ul>
44
- </nav>
45
-
46
- <section>
47
- <div class="columns">
48
- <article class="col-6 column">
13
+ <body>
14
+ <div class="container">
15
+ <nav>
16
+ <ul class="nav">
17
+ <li class="nav-item active">
18
+ <a href="#">Navigation</a>
19
+ <ul class="nav">
20
+ <% [ "Summary",
21
+ "Log Structure",
22
+ "Daily Distribution",
23
+ "Time Distribution",
24
+ "Most Requested Pages",
25
+ "Most Requested Resources",
26
+ "404 on HTML Files",
27
+ "404 on other Resources",
28
+ "Attacks",
29
+ "Statuses",
30
+ "Daily Statuses",
31
+ "Browsers",
32
+ "Platforms",
33
+ "Referers",
34
+ "IPs",
35
+ "Command Invocation",
36
+ "Performance"
37
+ ].each do |item| %>
38
+ <li class="nav-item">
39
+ <a href="#<%= item.downcase.gsub(' ', '-') %>"><%= item %></a>
40
+ </li>
41
+ <% end %>
42
+ </ul>
43
+ </li>
44
+ </ul>
45
+ <p>
46
+ Generated by<br />
47
+ <a href="https://www.ict4g.net/gitea/adolfo/apache_log_report">
48
+ Apache Log Report
49
+ </a> <br />
50
+ on <%= DateTime.now.strftime("%Y-%m-%d %H:%M") %>.<br />
51
+ The lean log analyzer.
52
+ </p>
53
+ </nav>
54
+
55
+ <section>
56
+ <h1>Apache Log Analysis: <%= data[:log_file] || "stdin" %></h1>
57
+
58
+ <div class="columns">
59
+ <article class="col-6 column">
49
60
  <h2 id="summary">Summary</h2>
50
61
 
51
62
  <table class="table summary">
63
+ <tr>
64
+ <th>Input file</th>
65
+ <td><b><%= (data[:log_file] || "stdin") %></b></td>
66
+ </tr>
67
+ <tr>
68
+ <th class="period">Period Analyzed</th>
69
+ <td class="period">
70
+ <%= data[:first_day_in_analysis] %>
71
+ --
72
+ <%= data[:last_day_in_analysis] %>
73
+ </td>
74
+ </tr>
75
+ <tr>
76
+ <th class="days">Days </th>
77
+ <td class="days"><%= data[:total_days_in_analysis] %></td>
78
+ </tr>
52
79
  <tr>
53
80
  <th class="hits">Hits</th>
54
81
  <td class="hits"><%= data[:total_hits][0][0] %></td>
@@ -61,21 +88,9 @@
61
88
  <th class="tx">Tx</th>
62
89
  <td class="tx"><%= data[:total_size][0][0] %></td>
63
90
  </tr>
64
- <tr>
65
- <th class="period">Period</th>
66
- <td class="period">
67
- <%= data[:first_day][0][0] %>
68
- --
69
- <%= data[:last_day][0][0] %>
70
- </td>
71
- </tr>
72
- <tr>
73
- <th class="days">Days </th>
74
- <td class="days"><%= data[:total_days] %></td>
75
- </tr>
76
91
  </table>
77
- </article>
78
- <article class="column col-6">
92
+ </article>
93
+ <article class="column col-6">
79
94
  <h2 id="log-structure">Log Structure</h2>
80
95
 
81
96
  <table class="table log-structure">
@@ -84,6 +99,14 @@
84
99
  <th>Input file</th>
85
100
  <td><b><%= (data[:log_file] || "stdin") %></b></td>
86
101
  </tr>
102
+ <tr>
103
+ <th>Period in Log</th>
104
+ <td><%= data[:first_day] %> -- <%= data[:last_day] %></td>
105
+ </tr>
106
+ <tr>
107
+ <th>Total days</th>
108
+ <td><%= data[:total_days] %></td>
109
+ </tr>
87
110
  <tr>
88
111
  <th>Log size</th>
89
112
  <td><%= data[:log_size][0][0] %></td>
@@ -102,50 +125,59 @@
102
125
  </tr>
103
126
  </tbody>
104
127
  </table>
105
- </article>
106
- </div>
107
-
108
- <% @reports = [
109
- { title: "Daily Distribution", header: ["Day", "Hits", "Visits", "Size"], rows: data[:daily_distribution] },
110
- { title: "Time Distribution", header: ["Hour", "Hits", "Visits", "Size"], rows: data[:time_distribution] },
111
- { title: "Most Requested Pages", header: ["Path", "Hits", "Visits", "Size"], rows: data[:most_requested_pages] },
112
- { title: "Most Requested Resources", header: ["Path", "Hits", "Visits", "Size"], rows: data[:most_requested_resources] },
113
- { title: "404 on HTML Files", header: ["Path", "Hits", "Visitors"], rows: data[:missed_pages] },
114
- { title: "404 on other Resources", header: ["Path", "Hits", "Visitors"], rows: data[:missed_resources] },
115
- { title: "Attacks", header: ["Path", "Hits", "Visitors"], rows: data[:attacks] },
116
- { },
117
- { title: "Statuses", header: ["Status", "Count"], rows: data[:statuses] },
118
- { title: "Daily Statuses", header: ["Status", "2xx", "3xx", "4xx"], rows: data[:statuses_by_day] },
119
- { title: "Browsers", header: ["Browser", "Hits", "Visitors", "Size"], rows: data[:browsers] },
120
- { title: "Platforms", header: ["Platform", "Hits", "Visitors", "Size"], rows: data[:platforms] },
121
- { title: "Referers", header: ["Referers", "Hits", "Visitors", "Size"], rows: data[:referers], col: "col-12" },
122
- { title: "IPs", header: ["IPs", "Hits", "Visitors", "Size"], rows: data[:ips] },
123
- { },
124
- ]
125
- %>
126
- <div class="columns">
127
- <% @reports.each do |report| %>
128
- <div class="column <%= report[:col] || "col-6" %>">
129
- <article>
130
- <% if report[:title] != nil %>
131
- <h2 id="<%= report[:title].downcase.gsub(/ +/, '-') %>">
132
- <%= report[:title] %>
133
- </h2>
134
- <%= render "output_table", report %>
135
- <% end %>
136
- </article>
137
- </div>
138
- <% end %>
139
- </div>
128
+ </article>
129
+ </div>
130
+
131
+ <% @reports = [
132
+ { title: "Daily Distribution", header: ["Day", "DOW", "Hits", "Visits", "Size"], rows: data[:daily_distribution] },
133
+ { title: "Time Distribution", header: ["Hour", "Hits", "Visits", "Size"], rows: data[:time_distribution] },
134
+ { title: "Most Requested Pages", header: ["Path", "Hits", "Visits", "Size"], rows: data[:most_requested_pages] },
135
+ { title: "Most Requested Resources", header: ["Path", "Hits", "Visits", "Size"], rows: data[:most_requested_resources] },
136
+ { title: "404 on HTML Files", header: ["Path", "Hits", "Visitors"], rows: data[:missed_pages] },
137
+ { title: "404 on other Resources", header: ["Path", "Hits", "Visitors"], rows: data[:missed_resources] },
138
+ { title: "Attacks", header: ["Path", "Hits", "Visitors"], rows: data[:attacks] },
139
+ { },
140
+ { title: "Statuses", header: ["Status", "Count"], rows: data[:statuses] },
141
+ { title: "Daily Statuses", header: ["Status", "2xx", "3xx", "4xx"], rows: data[:statuses_by_day] },
142
+ { title: "Browsers", header: ["Browser", "Hits", "Visitors", "Size"], rows: data[:browsers] },
143
+ { title: "Platforms", header: ["Platform", "Hits", "Visitors", "Size"], rows: data[:platforms] },
144
+ { title: "Referers", header: ["Referers", "Hits", "Visitors", "Size"], rows: data[:referers], col: "col-12" },
145
+ { title: "IPs", header: ["IPs", "Hits", "Visitors", "Size"], rows: data[:ips] },
146
+ { },
147
+ ]
148
+ %>
149
+ <div class="columns">
150
+ <% @reports.each do |report| %>
151
+ <div class="column <%= report[:col] || "col-6" %>">
152
+ <article>
153
+ <% if report[:title] != nil %>
154
+ <h2 id="<%= report[:title].downcase.gsub(/ +/, '-') %>">
155
+ <%= report[:title] %>
156
+ </h2>
157
+ <%= render "output_table", report %>
158
+ <% end %>
159
+ </article>
160
+ </div>
161
+ <% end %>
162
+ </div>
140
163
 
141
- <article>
164
+ <article>
142
165
  <h2 id="streaks">Streaks</h2>
143
166
 
144
167
  <table class="table streaks">
145
168
  <thead>
146
169
  <tr>
147
170
  <th>IP</th>
148
- <th>Day and URL</th>
171
+ <th>
172
+ <div class="columns">
173
+ <div class="col-2 column">
174
+ Day
175
+ </div>
176
+ <div class="col-10 column">
177
+ Resources
178
+ </div>
179
+ </div>
180
+ </th>
149
181
  </tr>
150
182
  </thead>
151
183
  <tbody>
@@ -153,28 +185,45 @@
153
185
  <tr>
154
186
  <td class="ip"><%= ip %></td>
155
187
  <td class="streaks">
156
- <% date_urls.group_by(&:first).each do |date, urls| %>
157
- <% urls.each do |url| %>
158
- <b><%= url[1] %>:</b> <%= url[2] %> <br />
159
- <% end %>
188
+ <div class="columns">
189
+ <% date_urls.group_by { |x| x[1] }.each do |date, urls| %>
190
+ <div class="col-2 column">
191
+ <%= date %>
192
+ </div>
193
+ <div class="col-10 column">
194
+ <span class="res-title">HTML:</span>
195
+ <ul>
196
+ <% urls.map { |x| x[2] }.select { |x| x.match /.*\.html?/ }.each do |url| %>
197
+ <li><%= url %></li>
198
+ <% end %>
199
+ </ul>
200
+
201
+ <span class="res-title">Other Resources:</span>
202
+ <ul>
203
+ <% urls.map { |x| x[2] }.sort.select { |x| not x.match /.*\.html?/ }.each do |url| %>
204
+ <li><%= url %></li>
205
+ <% end %>
206
+ </ul>
207
+ </div>
160
208
  <% end %>
209
+ </div>
161
210
  </td>
162
211
  </tr>
163
212
  <% end %>
164
213
  </tbody>
165
214
  </table>
166
- </article>
215
+ </article>
167
216
 
168
- <div class="columns">
169
- <div class="column col-6">
170
- <article>
217
+ <div class="columns">
218
+ <div class="column col-6">
219
+ <article>
171
220
  <h2 id="command-invocation">Command Invocation</h2>
172
221
 
173
222
  <table class="table command-invocation">
174
223
  <tbody>
175
224
  <tr>
176
225
  <th>CLI Command</th>
177
- <td><pre><%= data[:command] %></pre></td>
226
+ <td><code><%= data[:command] %></code></td>
178
227
  </tr>
179
228
  <tr>
180
229
  <th>Input file</th>
@@ -207,11 +256,11 @@
207
256
  </tr>
208
257
  </tbody>
209
258
  </table>
210
- </article>
211
- </div>
259
+ </article>
260
+ </div>
212
261
 
213
- <div class="column col-6">
214
- <article>
262
+ <div class="column col-6">
263
+ <article>
215
264
  <h2 id="performance"> Performance</h2>
216
265
 
217
266
  <table class="table performance">
@@ -241,10 +290,11 @@
241
290
  <td><%= "%.2f" % (data[:log_size][0][0] / data[:duration]) %></td></tr>
242
291
  </tbody>
243
292
  </table>
244
- </article>
293
+ </article>
294
+ </div>
245
295
  </div>
246
- </div>
247
- </section>
296
+ </section>
297
+ </div>
248
298
  </body>
249
299
  </html>
250
300
 
@@ -10,7 +10,11 @@
10
10
  | Hits | <%= "%10d" % data[:total_hits][0][0] %> |
11
11
  | Unique Visitors | <%= "%10d" % data[:total_unique_visitors][0][0] %> |
12
12
  | Tx | <%= "%10s" % data[:total_size][0][0] %> |
13
- | Days | <%= "%10d" % data[:total_days][0][0] %> |
13
+ | Logged Period | <%= data[:first_day] %> -- <%= data[:last_day] %> |
14
+ | Days | <%= "%10d" % data[:total_days] %> |
15
+ | Period Requested | <%= data[:first_day_requested] %> -- <%= data[:last_day_requested] %> |
16
+ | Period Analyzed | <%= data[:first_day_in_analysis] %> -- <%= data[:last_day_in_analysis] %> |
17
+ | Days in Analysis | <%= data[:total_days_in_analysis] %> |
14
18
 
15
19
  * Daily Distribution
16
20
 
@@ -1,3 +1,3 @@
1
1
  module ApacheLogReport
2
- VERSION = "1.1.4"
2
+ VERSION = "1.1.8"
3
3
  end
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: 1.1.4
4
+ version: 1.1.8
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-10-31 00:00:00.000000000 Z
11
+ date: 2021-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: apache_log-parser
@@ -119,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  - !ruby/object:Gem::Version
120
120
  version: '0'
121
121
  requirements: []
122
- rubygems_version: 3.2.22
122
+ rubygems_version: 3.0.3
123
123
  signing_key:
124
124
  specification_version: 4
125
125
  summary: Generate analytics from an Apache log file.