log_sense 2.1.0 → 2.2.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 +9 -0
- data/Gemfile.lock +1 -1
- data/exe/log_sense +2 -0
- data/lib/log_sense/rails/log_parser.rb +188 -37
- data/lib/log_sense/rails_aggregator.rb +15 -10
- data/lib/log_sense/rails_report_shaper.rb +35 -18
- data/lib/log_sense/report_shaper.rb +6 -1
- data/lib/log_sense/version.rb +1 -1
- data/todo.org +22 -5
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2acfa576d18214e05df5520518000955d12957756674c26a5fd63ac6efc9969
|
|
4
|
+
data.tar.gz: 781b38f2a7118efe8f0c1ca0cba6d21bdf0a77bd3fe27a8c1dc27ca5cdc2b3e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bc877d0198d6f170addfd9a0972ed7df57df2d5c00fdac850808ab1f8f86c3ed3d2aa37cc1058f1c6ca1da86bbdc6e6e09a44778edca3a48aaf825f4d5aefa26
|
|
7
|
+
data.tar.gz: ed99ed592a9a689fac9d319b1f346e705fcbbde021f45332d219097ed0df351d6b1d8a7bf2e0a72307b9932e2ab63ece7d0d8c2f2ef4fb9be45d14e2a5c6c863
|
data/CHANGELOG.org
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
#+AUTHOR: Adolfo Villafiorita
|
|
3
3
|
#+STARTUP: showall
|
|
4
4
|
|
|
5
|
+
* 2.2.1
|
|
6
|
+
|
|
7
|
+
- [Bug] Small fixes
|
|
8
|
+
|
|
9
|
+
* 2.2.0
|
|
10
|
+
|
|
11
|
+
- [User] Better management of Delayed Job (show also completed)
|
|
12
|
+
- [Refactoring] Rails rxegexps are now pre-compiled with /o
|
|
13
|
+
|
|
5
14
|
* 2.1.0
|
|
6
15
|
|
|
7
16
|
- [User] Delayed Job Errors
|
data/Gemfile.lock
CHANGED
data/exe/log_sense
CHANGED
|
@@ -165,6 +165,9 @@ module LogSense
|
|
|
165
165
|
# hash
|
|
166
166
|
pending = {}
|
|
167
167
|
|
|
168
|
+
# for delayed jobs
|
|
169
|
+
pending_jobs = {}
|
|
170
|
+
|
|
168
171
|
# Fatal explanation messages span several lines (2, 4, ?)
|
|
169
172
|
#
|
|
170
173
|
# We keep a Hash with the FATAL explanation messages and we persist when
|
|
@@ -304,15 +307,79 @@ module LogSense
|
|
|
304
307
|
next
|
|
305
308
|
end
|
|
306
309
|
|
|
310
|
+
#
|
|
311
|
+
# Match enqueuing job
|
|
312
|
+
#
|
|
313
|
+
data = match_and_process_enqueuing_job line
|
|
314
|
+
if data
|
|
315
|
+
id = data[:job_id]
|
|
316
|
+
pending_jobs[id] = data.merge(pending_jobs[id] || {})
|
|
317
|
+
next
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
#
|
|
321
|
+
# Match running
|
|
322
|
+
#
|
|
323
|
+
data = match_and_process_running_job line
|
|
324
|
+
if data
|
|
325
|
+
id = data[:job_id]
|
|
326
|
+
# change the key to pid
|
|
327
|
+
pid = data[:object_id]
|
|
328
|
+
pending_jobs[pid] = data.merge(pending_jobs[id] || {})
|
|
329
|
+
|
|
330
|
+
pending_jobs.delete(id)
|
|
331
|
+
next
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
#
|
|
335
|
+
# Match completed
|
|
336
|
+
#
|
|
337
|
+
data = match_and_process_completed_job line
|
|
338
|
+
if data
|
|
339
|
+
id = data[:object_id]
|
|
340
|
+
# it has to be there!
|
|
341
|
+
if pending_jobs[id]
|
|
342
|
+
data = (pending_jobs[id] || {}).merge(data)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
ins_job.execute(
|
|
346
|
+
data[:started_at],
|
|
347
|
+
data[:ended_at],
|
|
348
|
+
data[:duration_total_ms],
|
|
349
|
+
data[:worker],
|
|
350
|
+
data[:host],
|
|
351
|
+
data[:pid],
|
|
352
|
+
data[:log_id],
|
|
353
|
+
data[:job_id], # no longer necessary
|
|
354
|
+
data[:object_id], # completed jobs are destroyed
|
|
355
|
+
data[:method],
|
|
356
|
+
data[:arguments],
|
|
357
|
+
data[:exit_status],
|
|
358
|
+
data[:attempt],
|
|
359
|
+
data[:error_msg],
|
|
360
|
+
filename,
|
|
361
|
+
line_number
|
|
362
|
+
)
|
|
363
|
+
pending_jobs.delete(id)
|
|
364
|
+
next
|
|
365
|
+
end
|
|
366
|
+
|
|
307
367
|
#
|
|
308
368
|
# Match job errors
|
|
309
369
|
#
|
|
310
370
|
data = match_and_process_job_error line
|
|
311
371
|
if data
|
|
372
|
+
id = data[:object_id]
|
|
373
|
+
# TODO: no need for if (here and everywhere else)
|
|
374
|
+
# it has to be there!
|
|
375
|
+
if pending_jobs[id]
|
|
376
|
+
data = (pending_jobs[id] || {}).merge(data)
|
|
377
|
+
end
|
|
378
|
+
|
|
312
379
|
ins_job.execute(
|
|
313
|
-
data[:
|
|
380
|
+
data[:started_at],
|
|
314
381
|
data[:ended_at],
|
|
315
|
-
|
|
382
|
+
nil,
|
|
316
383
|
data[:worker],
|
|
317
384
|
data[:host],
|
|
318
385
|
data[:pid],
|
|
@@ -327,6 +394,8 @@ module LogSense
|
|
|
327
394
|
filename,
|
|
328
395
|
line_number
|
|
329
396
|
)
|
|
397
|
+
|
|
398
|
+
pending_jobs.delete(id)
|
|
330
399
|
end
|
|
331
400
|
end
|
|
332
401
|
end
|
|
@@ -337,23 +406,37 @@ module LogSense
|
|
|
337
406
|
ins_error.execute(value)
|
|
338
407
|
end
|
|
339
408
|
|
|
409
|
+
# DO NOT persist the pending_jobs which have not yet completed (those
|
|
410
|
+
# still available at: pending_jobs).
|
|
411
|
+
#
|
|
412
|
+
# In fact various entries initiated with RUNNING end up with "performed"
|
|
413
|
+
# (rather than COMPLETED). Notice that entries COMPLETED always have a
|
|
414
|
+
# peformed entry as well.
|
|
415
|
+
#
|
|
416
|
+
# Since we do not yet process "performed" entry log and in pending jobs
|
|
417
|
+
# we end up accumulating a bunch of entries which are marked as
|
|
418
|
+
# "performed"
|
|
419
|
+
#
|
|
420
|
+
# Performed entries are tricky since they use JOB_ID, rather than the
|
|
421
|
+
# object_id and probably requires to change how we enter pending_ids
|
|
422
|
+
|
|
340
423
|
db
|
|
341
424
|
end
|
|
342
425
|
|
|
343
426
|
# could be private here, I guess we keep them public to make them simpler
|
|
344
427
|
# to try from irb
|
|
345
428
|
|
|
346
|
-
TIMESTAMP =
|
|
347
|
-
LOG_ID =
|
|
348
|
-
VERB =
|
|
349
|
-
URL =
|
|
350
|
-
IP =
|
|
351
|
-
STATUS =
|
|
352
|
-
STATUS_IN_WORDS =
|
|
353
|
-
MSECS =
|
|
429
|
+
TIMESTAMP = '(?<timestamp>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)'
|
|
430
|
+
LOG_ID = '(?<log_id>[a-z0-9-]+)'
|
|
431
|
+
VERB = '(?<verb>GET|POST|PATCH|PUT|DELETE)'
|
|
432
|
+
URL = '(?<url>[^"]+)'
|
|
433
|
+
IP = '(?<ip>[0-9.]+)'
|
|
434
|
+
STATUS = '(?<status>[0-9]+)'
|
|
435
|
+
STATUS_IN_WORDS = '(OK|Unauthorized|Found|Internal Server Error|Bad Request|Method Not Allowed|Request Timeout|Not Implemented|Bad Gateway|Service Unavailable)'
|
|
436
|
+
MSECS = '[0-9.]+'
|
|
354
437
|
|
|
355
438
|
# I, [2021-10-19T08:16:34.343858 #10477] INFO -- : [67103c0d-455d-4fe8-951e-87e97628cb66] Started GET "/grow/people/471" for 217.77.80.35 at 2021-10-19 08:16:34 +0000
|
|
356
|
-
STARTED_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] Started #{VERB} "#{URL}" for #{IP} at/
|
|
439
|
+
STARTED_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] Started #{VERB} "#{URL}" for #{IP} at/o
|
|
357
440
|
|
|
358
441
|
def match_and_process_start(line)
|
|
359
442
|
matchdata = STARTED_REGEXP.match line
|
|
@@ -372,14 +455,14 @@ module LogSense
|
|
|
372
455
|
# I, [2021-10-19T08:16:34.712331 #10477] INFO -- : [67103c0d-455d-4fe8-951e-87e97628cb66] Completed 200 OK in 367ms (Views: 216.7ms | ActiveRecord: 141.3ms | Allocations: 168792)
|
|
373
456
|
# 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)
|
|
374
457
|
# I, [2021-12-06T14:28:19.736545 #2804090] INFO -- : [34091cb5-3e7b-4042-aaf8-6c6510d3f14c] Completed 500 Internal Server Error in 66ms (ActiveRecord: 8.0ms | Allocations: 24885)
|
|
375
|
-
COMPLETED_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] Completed #{STATUS} #{STATUS_IN_WORDS} in (?<total>#{MSECS})ms \((Views: (?<views>#{MSECS})ms \| )?ActiveRecord: (?<arec>#{MSECS})ms( \| Allocations: (?<alloc>[0-9]+))?\)/
|
|
458
|
+
COMPLETED_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] Completed #{STATUS} #{STATUS_IN_WORDS} in (?<total>#{MSECS})ms \((Views: (?<views>#{MSECS})ms \| )?ActiveRecord: (?<arec>#{MSECS})ms( \| Allocations: (?<alloc>[0-9]+))?\)/o
|
|
376
459
|
|
|
377
460
|
def match_and_process_completed(line)
|
|
378
461
|
matchdata = (COMPLETED_REGEXP.match line)
|
|
379
462
|
# exit_status = matchdata[:status].to_i == 500 ? "E" : "I"
|
|
380
463
|
if matchdata
|
|
381
464
|
{
|
|
382
|
-
exit_status: "
|
|
465
|
+
exit_status: "S:COMPLETED",
|
|
383
466
|
ended_at: matchdata[:timestamp],
|
|
384
467
|
log_id: matchdata[:log_id],
|
|
385
468
|
status: matchdata[:status],
|
|
@@ -393,7 +476,7 @@ module LogSense
|
|
|
393
476
|
end
|
|
394
477
|
|
|
395
478
|
# I, [2021-10-19T08:16:34.345162 #10477] INFO -- : [67103c0d-455d-4fe8-951e-87e97628cb66] Processing by PeopleController#show as HTML
|
|
396
|
-
PROCESSING_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] Processing by (?<controller>[^ ]+) as/
|
|
479
|
+
PROCESSING_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] Processing by (?<controller>[^ ]+) as/o
|
|
397
480
|
|
|
398
481
|
def match_and_process_processing_by line
|
|
399
482
|
matchdata = PROCESSING_REGEXP.match line
|
|
@@ -410,13 +493,13 @@ module LogSense
|
|
|
410
493
|
# F, [2021-12-04T00:34:05.839209 #2735058] FATAL -- : [3a16162e-a6a5-435e-a9d8-c4df5dc0f728]
|
|
411
494
|
# 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'
|
|
412
495
|
|
|
413
|
-
FATAL_REGEXP = /F, \[#{TIMESTAMP} #[0-9]+\] FATAL -- : \[#{LOG_ID}\]/
|
|
496
|
+
FATAL_REGEXP = /F, \[#{TIMESTAMP} #[0-9]+\] FATAL -- : \[#{LOG_ID}\]/o
|
|
414
497
|
|
|
415
498
|
def match_and_process_fatal(line)
|
|
416
499
|
matchdata = FATAL_REGEXP.match line
|
|
417
500
|
if matchdata
|
|
418
501
|
{
|
|
419
|
-
exit_status: "
|
|
502
|
+
exit_status: "S:FAILED",
|
|
420
503
|
log_id: matchdata[:log_id],
|
|
421
504
|
}
|
|
422
505
|
end
|
|
@@ -440,8 +523,8 @@ module LogSense
|
|
|
440
523
|
# [f57e3648-568a-48f9-ae3a-a522b1ff3298] app/models/donations/donation.rb:440:in `build_items_for_delivery'
|
|
441
524
|
# [f57e3648-568a-48f9-ae3a-a522b1ff3298] app/controllers/donations_controller.rb:1395:in `create_delivery'
|
|
442
525
|
|
|
443
|
-
EXCEPTION =
|
|
444
|
-
FATAL_EXPLANATION_REGEXP = /^\[#{LOG_ID}\] (?<context>#{EXCEPTION})?(?<description>.*)/
|
|
526
|
+
EXCEPTION = "[A-Za-z_0-9:]+(Error|NotFound|Invalid|Unknown|Missing|ENOSPC)"
|
|
527
|
+
FATAL_EXPLANATION_REGEXP = /^\[#{LOG_ID}\] (?<context>#{EXCEPTION})?(?<description>.*)/o
|
|
445
528
|
def match_and_process_fatal_explanation(line)
|
|
446
529
|
matchdata = FATAL_EXPLANATION_REGEXP.match line
|
|
447
530
|
if matchdata
|
|
@@ -454,7 +537,7 @@ module LogSense
|
|
|
454
537
|
end
|
|
455
538
|
|
|
456
539
|
# I, [2024-07-01T02:21:34.339058 #1392909] INFO -- : [815b3e28-8d6e-4741-8605-87654a9ff58c] BrowserInfo: "Unknown Browser","unknown_platform","Unknown","Devise::SessionsController","new","html","4db749654a0fcacbf3868f87723926e7405262f8d596e8514f4997dc80a3cd7e","2024-07-01T02:21:34+02:00"
|
|
457
|
-
BROWSER_INFO_REGEXP = /BrowserInfo: "(?<browser>.+)","(?<platform>.+)","(?<device_name>.+)","(?<controller>.+)","(?<method>.+)","(?<request_format>.+)","(?<anon_ip>.+)","(?<timestamp>.+)"/
|
|
540
|
+
BROWSER_INFO_REGEXP = /BrowserInfo: "(?<browser>.+)","(?<platform>.+)","(?<device_name>.+)","(?<controller>.+)","(?<method>.+)","(?<request_format>.+)","(?<anon_ip>.+)","(?<timestamp>.+)"/o
|
|
458
541
|
|
|
459
542
|
def match_and_process_browser_info(line)
|
|
460
543
|
matchdata = BROWSER_INFO_REGEXP.match line
|
|
@@ -474,12 +557,14 @@ module LogSense
|
|
|
474
557
|
|
|
475
558
|
# Sequence:
|
|
476
559
|
#
|
|
477
|
-
# - enqueued
|
|
478
|
-
# - running
|
|
479
|
-
# - performing
|
|
560
|
+
# - enqueued (LOG_ID user event, JOB_ID assigned by system)
|
|
561
|
+
# - running (JOB_ID links to enqueued; PID assigned by system; OBJECT_ID assigned by the system)
|
|
562
|
+
# - performing (OBJECT_ID links to running; OBJECT_ID assigned by the system; JOB_ID is new)
|
|
480
563
|
# - (rendering)
|
|
481
|
-
# - performed
|
|
482
|
-
# - completed
|
|
564
|
+
# - performed (OBJECT_ID links to running; JOB_ID links to previous)
|
|
565
|
+
# - completed (OBJECT_ID links to running; JOB_ID links to previous)
|
|
566
|
+
#
|
|
567
|
+
# SOMETIMES PERFORMED APPEARS WITH NO COMPLETED.
|
|
483
568
|
#
|
|
484
569
|
# I, [2024-08-01T06:21:16.302152 #3569287] INFO -- : [96d14192-c7cc-48a9-9df7-3786de20b085] [ActiveJob] Enqueued ActionMailer::Parameterized::DeliveryJob (Job ID: 01e82c5c-fb42-4e5f-b0a7-6fa9512a9fb5) to DelayedJob(mailers) with arguments: "MessageMailer", "build_message", "deliver_now", {:project_id=>1, :email_to=>"activpentrutine@gmail.com", :hash=>{:event_name=>"download", :subject=>"Aviz BRAC-MEGA240176", :download=>#<GlobalID:0x00007f02d8e1ad98 @uri=#<URI::GID gid://btf3/Download/10652>>, :group=>#<GlobalID:0x00007f02d8e1a820 @uri=#<URI::GID gid://btf3/Organization/10061>>}, :locale=>:ro}
|
|
485
570
|
#
|
|
@@ -489,41 +574,107 @@ module LogSense
|
|
|
489
574
|
#
|
|
490
575
|
# I, [2024-08-01T06:21:22.137863 #3563911] INFO -- : [ActiveJob] [ActionMailer::Parameterized::DeliveryJob] [01e82c5c-fb42-4e5f-b0a7-6fa9512a9fb5] Performed ActionMailer::Parameterized::DeliveryJob (Job ID: 01e82c5c-fb42-4e5f-b0a7-6fa9512a9fb5) from DelayedJob(mailers) in 886.42ms
|
|
491
576
|
#
|
|
577
|
+
#
|
|
578
|
+
# I, [2024-08-01T06:38:41.005687 #3563911] INFO -- : 2024-08-01T06:38:41+0200: [Worker(delayed_job host:shair1 pid:3563911)] 1 jobs processed at 1.4476 j/s, 0 failed
|
|
579
|
+
#
|
|
580
|
+
#
|
|
492
581
|
# I, [2024-08-01T06:21:22.141853 #3563911] INFO -- : 2024-08-01T06:21:22+0200: [Worker(delayed_job host:shair1 pid:3563911)] Job ActionMailer::Parameterized::DeliveryJob [01e82c5c-fb42-4e5f-b0a7-6fa9512a9fb5] from DelayedJob(mailers) with arguments: ["MessageMailer", "build_message", "deliver_now", {"project_id"=>1, "email_to"=>"activpentrutine@gmail.com", "hash"=>{"event_name"=>"download", "subject"=>"Aviz BRAC-MEGA240176", "download"=>{"_aj_globalid"=>"gid://btf3/Download/10652"}, "group"=>{"_aj_globalid"=>"gid://btf3/Organization/10061"}, "_aj_symbol_keys"=>["event_name", "subject", "download", "group"]}, "locale"=>{"_aj_serialized"=>"ActiveJob::Serializers::SymbolSerializer", "value"=>"ro"}, "_aj_symbol_keys"=>["project_id", "email_to", "hash", "locale"]}] (id=212885) (queue=mailers) COMPLETED after 0.9067
|
|
493
582
|
|
|
494
583
|
# Sequence with errors:
|
|
495
584
|
# (two log entries per error)
|
|
496
585
|
#
|
|
497
586
|
# E, [2024-08-15T05:10:30.613623 #4150573] ERROR -- : [ActiveJob] [ActionMailer::Parameterized::DeliveryJob] [79ea42c0-d280-4cf9-b77e-65917d4bc9fc] Error performing ActionMailer::Parameterized::DeliveryJob (Job ID: 79ea42c0-d280-4cf9-b77e-65917d4bc9fc) from DelayedJob(mailers) in 462.62ms: Net::SMTPFatalError (553 Recipient domain not specified.
|
|
587
|
+
|
|
498
588
|
# E, [2024-08-15T05:10:30.614189 #4150573] ERROR -- : 2024-08-15T05:10:30+0200: [Worker(delayed_job host:shair1 pid:4150573)] Job ActionMailer::Parameterized::DeliveryJob [79ea42c0-d280-4cf9-b77e-65917d4bc9fc] from DelayedJob(mailers) with arguments: ["MessageMailer", "build_message", "deliver_now", {"project_id"=>1, "email_to"=>"-", "hash"=>{"event_name"=>"download", "subject"=>"Aviz BvREWE240258.2", "download"=>{"_aj_globalid"=>"gid://btf3/Download/10877"}, "group"=>{"_aj_globalid"=>"gid://btf3/Organization/10060"}, "_aj_symbol_keys"=>["event_name", "subject", "download", "group"]}, "locale"=>{"_aj_serialized"=>"ActiveJob::Serializers::SymbolSerializer", "value"=>"ro"}, "_aj_symbol_keys"=>["project_id", "email_to", "hash", "locale"]}] (id=213242) (queue=mailers) FAILED (22 prior attempts) with Net::SMTPFatalError: 553 Recipient domain not specified.
|
|
499
589
|
|
|
500
|
-
TIMESTAMP_WITH_TZONE =
|
|
501
|
-
ID =
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
590
|
+
TIMESTAMP_WITH_TZONE = '(?<timestamp_tzone>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\+[0-9]+)'
|
|
591
|
+
ID = '(?<id>[0-9]+)'
|
|
592
|
+
JOB_ID = '(?<job_id>[a-zA-Z0-9-]+)'
|
|
593
|
+
WORKER = 'Worker\\((?<worker>.+) host:(?<host>.+) pid:(?<pid>[0-9]+)\\)'
|
|
594
|
+
METHOD = '(?<method>[A-Za-z0-9:#_]+)'
|
|
595
|
+
TIMES = '(?<attempt>[0-9]+)'
|
|
596
|
+
ERROR_MSG = '(?<error_msg>.+)'
|
|
597
|
+
ARGUMENTS = '(?<arguments>.+)'
|
|
507
598
|
|
|
508
|
-
|
|
599
|
+
#
|
|
600
|
+
# these are together, since they return temporary data
|
|
601
|
+
#
|
|
602
|
+
ENQUEUEING = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] \[ActiveJob\] Enqueued #{METHOD} \(Job ID: #{JOB_ID}\) to .* with arguments: #{ARGUMENTS}/o
|
|
509
603
|
|
|
510
|
-
|
|
604
|
+
def match_and_process_enqueuing_job(line)
|
|
605
|
+
matchdata = ENQUEUEING.match line
|
|
606
|
+
if matchdata
|
|
607
|
+
{
|
|
608
|
+
log_id: matchdata[:log_id],
|
|
609
|
+
job_id: matchdata[:job_id]
|
|
610
|
+
}
|
|
611
|
+
end
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
RUNNING_MESSAGE = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : #{TIMESTAMP_WITH_TZONE}: \[#{WORKER}\] Job #{METHOD} \[#{JOB_ID}\] from .+ with arguments: \[#{ARGUMENTS}\] \(id=#{ID}\) \(queue=.*\) RUNNING/o
|
|
615
|
+
|
|
616
|
+
def match_and_process_running_job(line)
|
|
617
|
+
matchdata = RUNNING_MESSAGE.match line
|
|
618
|
+
if matchdata
|
|
619
|
+
{
|
|
620
|
+
started_at: matchdata[:timestamp],
|
|
621
|
+
job_id: matchdata[:job_id],
|
|
622
|
+
object_id: matchdata[:id],
|
|
623
|
+
pid: matchdata[:pid]
|
|
624
|
+
}
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
COMPLETED_MESSAGE = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : #{TIMESTAMP_WITH_TZONE}: \[#{WORKER}\] Job #{METHOD} \[#{JOB_ID}\] from .+ with arguments: \[#{ARGUMENTS}\] \(id=#{ID}\) \(queue=.*\) COMPLETED after (?<duration_total_ms>#{MSECS})/o
|
|
629
|
+
|
|
630
|
+
def match_and_process_completed_job(line)
|
|
631
|
+
matchdata = COMPLETED_MESSAGE.match line
|
|
632
|
+
if matchdata
|
|
633
|
+
{
|
|
634
|
+
ended_at: matchdata[:timestamp],
|
|
635
|
+
duration_total_ms: matchdata[:duration_total_ms],
|
|
636
|
+
id: matchdata[:id],
|
|
637
|
+
job_id: matchdata[:job_id],
|
|
638
|
+
worker: matchdata[:worker],
|
|
639
|
+
host: matchdata[:host],
|
|
640
|
+
pid: matchdata[:pid],
|
|
641
|
+
log_id: matchdata.named_captures["log_id"],
|
|
642
|
+
object_id: matchdata[:id],
|
|
643
|
+
method: matchdata[:method],
|
|
644
|
+
exit_status: 'S:COMPLETED',
|
|
645
|
+
arguments: matchdata[:arguments]
|
|
646
|
+
}
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
# similar to completed with I->E, INFO->ERROR, COMPLETED->FAILED and final message structure a bit different
|
|
651
|
+
ERROR_MESSAGE_PERMANENT = /E, \[#{TIMESTAMP} #[0-9]+\] ERROR -- : #{TIMESTAMP_WITH_TZONE}: \[#{WORKER}\] Job #{METHOD} \[#{JOB_ID}\] from .+ with arguments: \[#{ARGUMENTS}\] \(id=#{ID}\) \(queue=.*\) (?<error_msg>FAILED permanently because of #{TIMES} consecutive failures)/o
|
|
652
|
+
|
|
653
|
+
ERROR_MESSAGE = /E, \[#{TIMESTAMP} #[0-9]+\] ERROR -- : #{TIMESTAMP_WITH_TZONE}: \[#{WORKER}\] Job #{METHOD} \[#{JOB_ID}\] from .+ with arguments: \[#{ARGUMENTS}\] \(id=#{ID}\) \(queue=.*\) FAILED \(#{TIMES} prior attempts\) with #{ERROR_MSG}/o
|
|
654
|
+
|
|
655
|
+
ERROR_MESSAGE_SHORT = /E, \[#{TIMESTAMP} #[0-9]+\] ERROR -- : #{TIMESTAMP_WITH_TZONE}: \[#{WORKER}\] Job #{METHOD} \(id=#{ID}\) FAILED \(#{TIMES} prior attempts\) with #{ERROR_MSG}/o
|
|
511
656
|
|
|
512
657
|
def match_and_process_job_error(line)
|
|
513
|
-
[ERROR_MESSAGE, ERROR_MESSAGE_SHORT].map do |regexp|
|
|
658
|
+
[ERROR_MESSAGE_PERMANENT, ERROR_MESSAGE, ERROR_MESSAGE_SHORT].map do |regexp|
|
|
514
659
|
matchdata = regexp.match line
|
|
515
660
|
if matchdata
|
|
661
|
+
exit_status = if matchdata[:error_msg].include?("permanently")
|
|
662
|
+
"S:FAILED"
|
|
663
|
+
else
|
|
664
|
+
"S:ERROR"
|
|
665
|
+
end
|
|
666
|
+
|
|
516
667
|
return {
|
|
517
668
|
ended_at: matchdata[:timestamp],
|
|
518
|
-
duration_total_ms:
|
|
669
|
+
duration_total_ms: nil, # we could compute the time to failure
|
|
519
670
|
worker: matchdata[:worker],
|
|
520
671
|
host: matchdata[:host],
|
|
521
672
|
pid: matchdata[:pid],
|
|
522
|
-
|
|
673
|
+
job_id: matchdata.named_captures["job_id"],
|
|
523
674
|
object_id: matchdata[:id],
|
|
524
675
|
method: matchdata[:method],
|
|
525
676
|
arguments: matchdata.named_captures["arguments"],
|
|
526
|
-
exit_status
|
|
677
|
+
exit_status:,
|
|
527
678
|
attempt: matchdata[:attempt],
|
|
528
679
|
error_msg: matchdata[:error_msg],
|
|
529
680
|
}
|
|
@@ -108,7 +108,7 @@ module LogSense
|
|
|
108
108
|
sum(iif(context NOT LIKE '%ActionController::RoutingError%', 1, 0)) as OtherErrors
|
|
109
109
|
FROM Event JOIN Error
|
|
110
110
|
ON event.log_id == error.log_id
|
|
111
|
-
WHERE #{filter} and exit_status == '
|
|
111
|
+
WHERE #{filter} and exit_status == 'S:FAILED'
|
|
112
112
|
GROUP BY strftime("%Y-%m-%d", started_at)
|
|
113
113
|
).gsub("\n", "") || [[]]
|
|
114
114
|
|
|
@@ -121,7 +121,7 @@ module LogSense
|
|
|
121
121
|
event.log_id
|
|
122
122
|
FROM Event JOIN Error
|
|
123
123
|
ON event.log_id == error.log_id
|
|
124
|
-
WHERE #{filter} and exit_status == '
|
|
124
|
+
WHERE #{filter} and exit_status == 'S:FAILED'
|
|
125
125
|
).gsub("\n", "") || [[]]
|
|
126
126
|
|
|
127
127
|
@fatal_grouped = @db.execute %(
|
|
@@ -134,20 +134,24 @@ module LogSense
|
|
|
134
134
|
GROUP BY description
|
|
135
135
|
).gsub("\n", "") || [[]]
|
|
136
136
|
|
|
137
|
-
@
|
|
137
|
+
@job_plot = @db.execute %(
|
|
138
138
|
SELECT strftime("%Y-%m-%d", ended_at) as Day,
|
|
139
|
-
|
|
139
|
+
sum(iif(exit_status == 'S:COMPLETED', 1, 0)) as Completed,
|
|
140
|
+
sum(iif(exit_status == 'S:ERROR' or exit_status == 'S:FAILED', 1, 0)) as Errors
|
|
140
141
|
FROM Job
|
|
141
142
|
WHERE #{filter}
|
|
142
143
|
GROUP BY strftime("%Y-%m-%d", ended_at)
|
|
143
144
|
).gsub("\n", "") || [[]]
|
|
144
145
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
# worker,
|
|
147
|
+
# host,
|
|
148
|
+
# pid,
|
|
149
|
+
@jobs = @db.execute %(
|
|
150
|
+
SELECT strftime("%Y-%m-%d %H:%M", started_at),
|
|
151
|
+
duration_total_ms,
|
|
149
152
|
pid,
|
|
150
153
|
object_id,
|
|
154
|
+
exit_status,
|
|
151
155
|
method,
|
|
152
156
|
arguments,
|
|
153
157
|
error_msg,
|
|
@@ -161,12 +165,13 @@ module LogSense
|
|
|
161
165
|
host,
|
|
162
166
|
pid,
|
|
163
167
|
object_id,
|
|
168
|
+
exit_status,
|
|
169
|
+
GROUP_CONCAT(DISTINCT(error_msg)),
|
|
164
170
|
method,
|
|
165
171
|
arguments,
|
|
166
|
-
error_msg,
|
|
167
172
|
max(attempt)
|
|
168
173
|
FROM Job
|
|
169
|
-
WHERE #{filter}
|
|
174
|
+
WHERE #{filter} and (exit_status == 'S:ERROR' or exit_status == 'S:FAILED')
|
|
170
175
|
GROUP BY object_id
|
|
171
176
|
).gsub("\n", "") || [[]]
|
|
172
177
|
|
|
@@ -182,7 +182,7 @@ module LogSense
|
|
|
182
182
|
name: 'Routing Errors',
|
|
183
183
|
data: fatal_plot.filter(row => row[0] != '').map(row => row[2]),
|
|
184
184
|
type: 'bar',
|
|
185
|
-
color: '#
|
|
185
|
+
color: '#D0D0D0',
|
|
186
186
|
label: {
|
|
187
187
|
show: true,
|
|
188
188
|
position: 'top'
|
|
@@ -209,14 +209,27 @@ module LogSense
|
|
|
209
209
|
rows: data[:fatal_grouped],
|
|
210
210
|
col: "small-12 cell"
|
|
211
211
|
},
|
|
212
|
+
browsers(data),
|
|
213
|
+
platforms(data),
|
|
214
|
+
ips(data),
|
|
215
|
+
countries(data),
|
|
216
|
+
ip_per_hour_report_spec(ips_per_hour(data[:ips_per_hour])),
|
|
217
|
+
session_report_spec(ips_detailed(data[:ips_per_day_detailed])),
|
|
212
218
|
{
|
|
213
|
-
title: "
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
219
|
+
title: "Jobs (Completed and Failed)",
|
|
220
|
+
explanation: %(
|
|
221
|
+
This report includes completed and failed jobs, parsing lines
|
|
222
|
+
marked as COMPLETED or ERROR/FAILED.
|
|
223
|
+
|
|
224
|
+
This excludes from the table entries marked as RUNNING and then
|
|
225
|
+
completed with "performed".
|
|
226
|
+
),
|
|
227
|
+
header: %w[Date Duration PID ID Exit_Status Method Arguments Error_Msg Attempts],
|
|
228
|
+
column_alignment: %i[left right left left left left left left right],
|
|
229
|
+
column_width: ["10%", "5%", "5%", "5%", "5%", "15%", "25%", "25%", "5%"],
|
|
230
|
+
rows: data[:jobs],
|
|
218
231
|
col: "small-12 cell",
|
|
219
|
-
echarts_extra: "var fatal_plot=#{data[:
|
|
232
|
+
echarts_extra: "var fatal_plot=#{data[:job_plot].to_json}",
|
|
220
233
|
echarts_spec: "{
|
|
221
234
|
toolbox: {
|
|
222
235
|
feature: {
|
|
@@ -241,9 +254,19 @@ module LogSense
|
|
|
241
254
|
},
|
|
242
255
|
series: [
|
|
243
256
|
{
|
|
244
|
-
name: '
|
|
257
|
+
name: 'Completed',
|
|
245
258
|
data: fatal_plot.filter(row => row[0] != '').map(row => row[1]),
|
|
246
259
|
type: 'bar',
|
|
260
|
+
color: '#D0D0D0',
|
|
261
|
+
label: {
|
|
262
|
+
show: true,
|
|
263
|
+
position: 'top'
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'Errors',
|
|
268
|
+
data: fatal_plot.filter(row => row[0] != '').map(row => row[2]),
|
|
269
|
+
type: 'bar',
|
|
247
270
|
color: '#D30001',
|
|
248
271
|
label: {
|
|
249
272
|
show: true,
|
|
@@ -255,18 +278,12 @@ module LogSense
|
|
|
255
278
|
},
|
|
256
279
|
{
|
|
257
280
|
title: "Job Errors (grouped)",
|
|
258
|
-
header: %w[Worker Host PID ID Error Method Arguments
|
|
259
|
-
column_alignment: %i[left left left left left left left right],
|
|
260
|
-
column_width: ["5%", "5%", "5%", "5%", "20%", "
|
|
281
|
+
header: %w[Worker Host PID ID Exit_Status Error Method Arguments Attempts],
|
|
282
|
+
column_alignment: %i[left left left left left left left left right],
|
|
283
|
+
column_width: ["5%", "5%", "5%", "5%", "5%", "20%", "25%", "20%", "10%"],
|
|
261
284
|
rows: data[:job_error_grouped],
|
|
262
285
|
col: "small-12 cell"
|
|
263
|
-
}
|
|
264
|
-
browsers(data),
|
|
265
|
-
platforms(data),
|
|
266
|
-
ips(data),
|
|
267
|
-
countries(data),
|
|
268
|
-
ip_per_hour_report_spec(ips_per_hour(data[:ips_per_hour])),
|
|
269
|
-
session_report_spec(ips_detailed(data[:ips_per_day_detailed]))
|
|
286
|
+
}
|
|
270
287
|
]
|
|
271
288
|
end
|
|
272
289
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module LogSense
|
|
2
2
|
class ReportShaper
|
|
3
|
+
SESSION_URL_LIMIT = 300
|
|
3
4
|
WORDS_SEPARATOR = ' · '
|
|
4
5
|
|
|
5
6
|
# return { [ip,day] => { [ hits, list of urls ] } }
|
|
@@ -14,7 +15,11 @@ module LogSense
|
|
|
14
15
|
date,
|
|
15
16
|
hash[ip][date].size,
|
|
16
17
|
hash[ip][date].uniq.size,
|
|
17
|
-
hash[ip][date].uniq.size <
|
|
18
|
+
if hash[ip][date].uniq.size < SESSION_URL_LIMIT
|
|
19
|
+
hash[ip][date].uniq.join(WORDS_SEPARATOR)
|
|
20
|
+
else
|
|
21
|
+
"[too many]"
|
|
22
|
+
end
|
|
18
23
|
]
|
|
19
24
|
end
|
|
20
25
|
end
|
data/lib/log_sense/version.rb
CHANGED
data/todo.org
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
* Todo (2024-08-01)
|
|
4
4
|
|
|
5
|
+
** T Break rails log_parser in subclasses (one per log type)
|
|
6
|
+
|
|
5
7
|
** T Distinguish between exceptions, errors with no timestamps and errors with timestamps :feature:
|
|
6
8
|
- In rails reports some errors don't have a timestamp, whereas other have.
|
|
7
9
|
|
|
8
10
|
1. Check whether we can always get an ID of an event
|
|
9
|
-
2.
|
|
10
|
-
|
|
11
11
|
|
|
12
12
|
** T Move geolocation to aggregator, so that we can perform queries more efficiently :refactoring:
|
|
13
13
|
** T Filter on dates :feature:
|
|
@@ -18,9 +18,7 @@
|
|
|
18
18
|
** T error on data
|
|
19
19
|
- The data reported by echarts on the number of hits of a controller
|
|
20
20
|
differs from the data shown in the table?
|
|
21
|
-
** T
|
|
22
|
-
** T Dark style
|
|
23
|
-
** T Remove dependency from Zurb Foundation (native css grid instead)
|
|
21
|
+
** T Remove dependency from Zurb Foundation (native css grid instead) :refactoring:
|
|
24
22
|
** T refactor report specifications in their own class :refactoring:
|
|
25
23
|
** T Add lines not parsed to the report :feature:
|
|
26
24
|
** T Using an empty log as input raises an error :feature:
|
|
@@ -314,3 +312,22 @@
|
|
|
314
312
|
:ARCHIVE_CATEGORY: todo
|
|
315
313
|
:ARCHIVE_TODO: REJECTED
|
|
316
314
|
:END:
|
|
315
|
+
|
|
316
|
+
** D Dark style
|
|
317
|
+
:PROPERTIES:
|
|
318
|
+
:ARCHIVE_TIME: 2024-08-23 Fri 16:25
|
|
319
|
+
:ARCHIVE_FILE: ~/Sources/ruby/log_sense/todo.org
|
|
320
|
+
:ARCHIVE_OLPATH: Todo (2024-08-01)
|
|
321
|
+
:ARCHIVE_CATEGORY: todo
|
|
322
|
+
:ARCHIVE_TODO: D
|
|
323
|
+
:END:
|
|
324
|
+
|
|
325
|
+
** D Sidebar foreground color in new apache report
|
|
326
|
+
:PROPERTIES:
|
|
327
|
+
:ARCHIVE_TIME: 2024-08-23 Fri 16:25
|
|
328
|
+
:ARCHIVE_FILE: ~/Sources/ruby/log_sense/todo.org
|
|
329
|
+
:ARCHIVE_OLPATH: Todo (2024-08-01)
|
|
330
|
+
:ARCHIVE_CATEGORY: todo
|
|
331
|
+
:ARCHIVE_TODO: D
|
|
332
|
+
:END:
|
|
333
|
+
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: log_sense
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1
|
|
4
|
+
version: 2.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adolfo Villafiorita
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-08-
|
|
11
|
+
date: 2024-08-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: browser
|