log_sense 1.0.7 → 1.0.8
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/.gitignore +1 -1
- data/lib/log_sense/ip_locator.rb +2 -0
- data/lib/log_sense/options_parser.rb +2 -1
- data/lib/log_sense/rails_data_cruncher.rb +4 -2
- data/lib/log_sense/rails_log_parser.rb +122 -6
- data/lib/log_sense/templates/_summary.html.erb +1 -1
- data/lib/log_sense/templates/_summary.txt.erb +1 -1
- data/lib/log_sense/templates/rails.txt.erb +6 -0
- data/lib/log_sense/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45cf68bf2165554a6392a9277c121952e05dd9a283fe59b881ef957f3682920e
|
4
|
+
data.tar.gz: 99ded91c60080ffbfda3e60f05ee4b058e215286adfccdb2a727a50d2c7d0467
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45674016ae3c3110584e967dd60166391a07b1401186428e7e949c012bfc883686110e86afeec5450e03d34a8b7831c6bbc1d1b9393215808a2d1feafcd6422b
|
7
|
+
data.tar.gz: a4e9b7bf983d23ba4e8ac744621e34c5b83eee45076cf165ba9eb856d5687d47191ab048be6fed0ca4316448c84d8f80e1b7daedb518d94a90b52b7f94ee2077
|
data/.gitignore
CHANGED
data/lib/log_sense/ip_locator.rb
CHANGED
@@ -22,7 +22,7 @@ module LogSense
|
|
22
22
|
args[:input_file] = n
|
23
23
|
end
|
24
24
|
|
25
|
-
opts.on("-tFORMAT", "--output-format=FORMAT", String, "Output format: html, org, txt, sqlite.
|
25
|
+
opts.on("-tFORMAT", "--output-format=FORMAT", String, "Output format: html, org, txt, sqlite. See below for available formats") do |n|
|
26
26
|
args[:output_format] = n
|
27
27
|
end
|
28
28
|
|
@@ -74,6 +74,7 @@ module LogSense
|
|
74
74
|
components = templates.map { |x| File.basename(x).split "." }.group_by { |x| x[0] }
|
75
75
|
components.each do |k, vs|
|
76
76
|
puts "#{k} parsing can produce the following outputs:"
|
77
|
+
puts " - sqlite"
|
77
78
|
vs.each do |v|
|
78
79
|
puts " - #{v[1]}"
|
79
80
|
end
|
@@ -15,8 +15,8 @@ module LogSense
|
|
15
15
|
|
16
16
|
# make first and last day into dates or nil
|
17
17
|
# TODO: bug possible value here: [[nil]], which is not empty
|
18
|
-
@first_day =
|
19
|
-
@last_day =
|
18
|
+
@first_day = first_day_s&.first&.first ? Date.parse(first_day_s[0][0]) : nil
|
19
|
+
@last_day = last_day_s&.first&.first ? Date.parse(last_day_s[0][0]) : nil
|
20
20
|
|
21
21
|
@total_days = 0
|
22
22
|
if @first_day and @last_day
|
@@ -100,6 +100,8 @@ module LogSense
|
|
100
100
|
|
101
101
|
@performance = db.execute "SELECT distinct(controller), count(controller), printf(\"%.2f\", min(duration_total_ms)), printf(\"%.2f\", avg(duration_total_ms)), printf(\"%.2f\", max(duration_total_ms)) from Event group by controller order by controller"
|
102
102
|
|
103
|
+
@fatal = db.execute "SELECT strftime(\"%Y-%m-%d %H:%M\", started_at), ip, url, log_id FROM Event WHERE exit_status == 'F'"
|
104
|
+
|
103
105
|
data = {}
|
104
106
|
self.instance_variables.each do |variable|
|
105
107
|
var_as_symbol = variable.to_s[1..-1].to_sym
|
@@ -8,6 +8,7 @@ module LogSense
|
|
8
8
|
db = SQLite3::Database.new ":memory:"
|
9
9
|
db.execute 'CREATE TABLE IF NOT EXISTS Event(
|
10
10
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
11
|
+
exit_status TEXT,
|
11
12
|
started_at TEXT,
|
12
13
|
ended_at TEXT,
|
13
14
|
log_id TEXT,
|
@@ -20,10 +21,12 @@ module LogSense
|
|
20
21
|
duration_total_ms FLOAT,
|
21
22
|
duration_views_ms FLOAT,
|
22
23
|
duration_ar_ms FLOAT,
|
23
|
-
allocations INTEGER
|
24
|
+
allocations INTEGER,
|
25
|
+
comment TEXT
|
24
26
|
)'
|
25
27
|
|
26
28
|
ins = db.prepare("insert into Event(
|
29
|
+
exit_status,
|
27
30
|
started_at,
|
28
31
|
ended_at,
|
29
32
|
log_id,
|
@@ -36,8 +39,10 @@ module LogSense
|
|
36
39
|
duration_total_ms,
|
37
40
|
duration_views_ms,
|
38
41
|
duration_ar_ms,
|
39
|
-
allocations
|
40
|
-
|
42
|
+
allocations,
|
43
|
+
comment
|
44
|
+
)
|
45
|
+
values (#{Array.new(15, '?').join(', ')})")
|
41
46
|
|
42
47
|
# requests in the log might be interleaved.
|
43
48
|
#
|
@@ -61,7 +66,7 @@ module LogSense
|
|
61
66
|
|
62
67
|
File.readlines(filename).each do |line|
|
63
68
|
# We discard LOG_LEVEL != 'I'
|
64
|
-
next if line[0] != 'I'
|
69
|
+
next if line[0] != 'I' and line[0] != 'F'
|
65
70
|
|
66
71
|
data = self.match_and_process_start line
|
67
72
|
if data
|
@@ -77,6 +82,37 @@ module LogSense
|
|
77
82
|
next
|
78
83
|
end
|
79
84
|
|
85
|
+
data = self.match_and_process_fatal line
|
86
|
+
if data
|
87
|
+
id = data[:log_id]
|
88
|
+
# it might as well be that the first event started before
|
89
|
+
# the log. With this, we make sure we add only events whose
|
90
|
+
# start was logged and parsed
|
91
|
+
if pending[id]
|
92
|
+
event = data.merge (pending[id] || {})
|
93
|
+
|
94
|
+
ins.execute(
|
95
|
+
event[:exit_status],
|
96
|
+
event[:started_at],
|
97
|
+
event[:ended_at],
|
98
|
+
event[:log_id],
|
99
|
+
event[:ip],
|
100
|
+
"#{DateTime.parse(event[:started_at]).strftime("%Y-%m-%d")} #{event[:ip]}",
|
101
|
+
event[:url],
|
102
|
+
event[:controller],
|
103
|
+
event[:html_verb],
|
104
|
+
event[:status],
|
105
|
+
event[:duration_total_ms],
|
106
|
+
event[:duration_views_ms],
|
107
|
+
event[:duration_ar_ms],
|
108
|
+
event[:allocations],
|
109
|
+
event[:comment]
|
110
|
+
)
|
111
|
+
|
112
|
+
pending.delete(id)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
80
116
|
data = self.match_and_process_completed line
|
81
117
|
if data
|
82
118
|
id = data[:log_id]
|
@@ -88,6 +124,40 @@ module LogSense
|
|
88
124
|
event = data.merge (pending[id] || {})
|
89
125
|
|
90
126
|
ins.execute(
|
127
|
+
event[:exit_status],
|
128
|
+
event[:started_at],
|
129
|
+
event[:ended_at],
|
130
|
+
event[:log_id],
|
131
|
+
event[:ip],
|
132
|
+
"#{DateTime.parse(event[:started_at]).strftime("%Y-%m-%d")} #{event[:ip]}",
|
133
|
+
event[:url],
|
134
|
+
event[:controller],
|
135
|
+
event[:html_verb],
|
136
|
+
event[:status],
|
137
|
+
event[:duration_total_ms],
|
138
|
+
event[:duration_views_ms],
|
139
|
+
event[:duration_ar_ms],
|
140
|
+
event[:allocations],
|
141
|
+
event[:comment]
|
142
|
+
)
|
143
|
+
|
144
|
+
pending.delete(id)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
data = self.match_and_process_completed_no_alloc line
|
150
|
+
if data
|
151
|
+
id = data[:log_id]
|
152
|
+
|
153
|
+
# it might as well be that the first event started before
|
154
|
+
# the log. With this, we make sure we add only events whose
|
155
|
+
# start was logged and parsed
|
156
|
+
if pending[id]
|
157
|
+
event = data.merge (pending[id] || {})
|
158
|
+
|
159
|
+
ins.execute(
|
160
|
+
event[:exit_status],
|
91
161
|
event[:started_at],
|
92
162
|
event[:ended_at],
|
93
163
|
event[:log_id],
|
@@ -100,12 +170,14 @@ module LogSense
|
|
100
170
|
event[:duration_total_ms],
|
101
171
|
event[:duration_views_ms],
|
102
172
|
event[:duration_ar_ms],
|
103
|
-
event[:allocations]
|
173
|
+
event[:allocations],
|
174
|
+
event[:comment]
|
104
175
|
)
|
105
176
|
|
106
177
|
pending.delete(id)
|
107
178
|
end
|
108
179
|
end
|
180
|
+
|
109
181
|
end
|
110
182
|
|
111
183
|
db
|
@@ -141,9 +213,10 @@ module LogSense
|
|
141
213
|
COMPLETED_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{ID}\] Completed #{STATUS} [^ ]+ in (?<total>#{MSECS})ms \(Views: (?<views>#{MSECS})ms \| ActiveRecord: (?<arec>#{MSECS})ms \| Allocations: (?<alloc>[0-9]+)\)/
|
142
214
|
|
143
215
|
def self.match_and_process_completed line
|
144
|
-
matchdata = COMPLETED_REGEXP.match line
|
216
|
+
matchdata = (COMPLETED_REGEXP.match line)
|
145
217
|
if matchdata
|
146
218
|
{
|
219
|
+
exit_status: "I",
|
147
220
|
ended_at: matchdata[:timestamp],
|
148
221
|
log_id: matchdata[:id],
|
149
222
|
status: matchdata[:status],
|
@@ -151,12 +224,36 @@ module LogSense
|
|
151
224
|
duration_views_ms: matchdata[:views],
|
152
225
|
duration_ar_ms: matchdata[:arec],
|
153
226
|
allocations: matchdata[:alloc],
|
227
|
+
comment: ""
|
228
|
+
}
|
229
|
+
else
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# I, [2021-12-09T16:53:52.657727 #2735058] INFO -- : [0064e403-9eb2-439d-8fe1-a334c86f5532] Completed 200 OK in 13ms (Views: 11.1ms | ActiveRecord: 1.2ms)
|
235
|
+
COMPLETED_NO_ALLOC_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{ID}\] Completed #{STATUS} [^ ]+ in (?<total>#{MSECS})ms \(Views: (?<views>#{MSECS})ms \| ActiveRecord: (?<arec>#{MSECS})ms\)/
|
236
|
+
|
237
|
+
def self.match_and_process_completed_no_alloc line
|
238
|
+
matchdata = (COMPLETED_NO_ALLOC_REGEXP.match line)
|
239
|
+
if matchdata
|
240
|
+
{
|
241
|
+
exit_status: "I",
|
242
|
+
ended_at: matchdata[:timestamp],
|
243
|
+
log_id: matchdata[:id],
|
244
|
+
status: matchdata[:status],
|
245
|
+
duration_total_ms: matchdata[:total],
|
246
|
+
duration_views_ms: matchdata[:views],
|
247
|
+
duration_ar_ms: matchdata[:arec],
|
248
|
+
allocations: -1,
|
249
|
+
comment: ""
|
154
250
|
}
|
155
251
|
else
|
156
252
|
nil
|
157
253
|
end
|
158
254
|
end
|
159
255
|
|
256
|
+
|
160
257
|
# I, [2021-10-19T08:16:34.345162 #10477] INFO -- : [67103c0d-455d-4fe8-951e-87e97628cb66] Processing by PeopleController#show as HTML
|
161
258
|
PROCESSING_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{ID}\] Processing by (?<controller>[^ ]+) as/
|
162
259
|
|
@@ -172,6 +269,25 @@ module LogSense
|
|
172
269
|
end
|
173
270
|
end
|
174
271
|
|
272
|
+
# F, [2021-12-04T00:34:05.838973 #2735058] FATAL -- : [3a16162e-a6a5-435e-a9d8-c4df5dc0f728]
|
273
|
+
# F, [2021-12-04T00:34:05.839157 #2735058] FATAL -- : [3a16162e-a6a5-435e-a9d8-c4df5dc0f728] ActionController::RoutingError (No route matches [GET] "/wp/wp-includes/wlwmanifest.xml"):
|
274
|
+
# F, [2021-12-04T00:34:05.839209 #2735058] FATAL -- : [3a16162e-a6a5-435e-a9d8-c4df5dc0f728]
|
275
|
+
# F, [2021-12-04T00:34:05.839269 #2735058] FATAL -- : [3a16162e-a6a5-435e-a9d8-c4df5dc0f728] actionpack (5.2.4.4) lib/action_dispatch/middleware/debug_exceptions.rb:65:in `call'
|
276
|
+
FATAL_REGEXP = /F, \[#{TIMESTAMP} #[0-9]+\] FATAL -- : \[#{ID}\] (?<comment>.*)$/
|
277
|
+
|
278
|
+
def self.match_and_process_fatal line
|
279
|
+
matchdata = FATAL_REGEXP.match line
|
280
|
+
if matchdata
|
281
|
+
{
|
282
|
+
exit_status: "F",
|
283
|
+
log_id: matchdata[:id],
|
284
|
+
comment: matchdata[:comment]
|
285
|
+
}
|
286
|
+
else
|
287
|
+
nil
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
175
291
|
end
|
176
292
|
|
177
293
|
end
|
@@ -25,7 +25,7 @@
|
|
25
25
|
</tr>
|
26
26
|
<tr>
|
27
27
|
<th class="avg-hits-per-unique-visits">Unique Visits</th>
|
28
|
-
<td class="avg-hits-per-unique-visits"><%= data[:total_hits] / data[:total_unique_visits] %></td>
|
28
|
+
<td class="avg-hits-per-unique-visits"><%= data[:total_unique_visits] != 0 ? data[:total_hits] / data[:total_unique_visits] : "N/A" %></td>
|
29
29
|
</tr>
|
30
30
|
<tr>
|
31
31
|
<th class="tx">Tx</th>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
["Days", data[:total_days_in_analysis] ],
|
5
5
|
["Events", data[:events] ],
|
6
6
|
["Unique Visits", data[:total_unique_visits] ],
|
7
|
-
["Avg. Events per Visit", data[:events] / data[:total_unique_visits] ]
|
7
|
+
["Avg. Events per Visit", data[:total_unique_visits] != 0 ? data[:events] / data[:total_unique_visits] : "N/A" ]
|
8
8
|
]
|
9
9
|
table
|
10
10
|
%>
|
@@ -44,6 +44,12 @@ table.align_column(4, :right)
|
|
44
44
|
table
|
45
45
|
%>
|
46
46
|
|
47
|
+
** Fatal Events
|
48
|
+
|
49
|
+
<%= table = Terminal::Table.new headings: ['Date', 'IP', 'URL', 'Log ID'], rows: @data[:fatal]
|
50
|
+
table
|
51
|
+
%>
|
52
|
+
|
47
53
|
** Command Invocation
|
48
54
|
|
49
55
|
<%= render "command_invocation.txt.erb", data: data %>
|
data/lib/log_sense/version.rb
CHANGED