log_sense 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.org +5 -0
- data/Gemfile.lock +1 -1
- data/exe/log_sense +2 -0
- data/lib/log_sense/rails/log_parser.rb +178 -33
- data/lib/log_sense/rails_aggregator.rb +13 -8
- data/lib/log_sense/rails_report_shaper.rb +33 -16
- 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: 8b24aa444f64beeb8b13bda22aae730130cff025b4d122557499d3afd2ec2e73
|
4
|
+
data.tar.gz: 809c695be701f9a6f17a4b31d4c9c61827a97a9088cb180b6f7f60a794e498f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afff457f8f952f7f6b806baaf56ac34010027498be04fdbfaa91653ab70699f3d3d85222b47437c2df151f00fb953e23d21a5ac537562cf14a157459762e4855
|
7
|
+
data.tar.gz: 911594383266801861454d6facb9ee4c2db852979d2349250d988934721925ed6592b9fdbb2c63d43814305ca062478d8984bd07166ff90c3928120dc17c3129
|
data/CHANGELOG.org
CHANGED
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,13 +307,75 @@ 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
|
+
# it has to be there!
|
373
|
+
if pending_jobs[id]
|
374
|
+
data = (pending_jobs[id] || {}).merge(data)
|
375
|
+
end
|
376
|
+
|
312
377
|
ins_job.execute(
|
313
|
-
data[:
|
378
|
+
data[:started_at],
|
314
379
|
data[:ended_at],
|
315
380
|
0,
|
316
381
|
data[:worker],
|
@@ -327,6 +392,8 @@ module LogSense
|
|
327
392
|
filename,
|
328
393
|
line_number
|
329
394
|
)
|
395
|
+
|
396
|
+
pending_jobs.delete(id)
|
330
397
|
end
|
331
398
|
end
|
332
399
|
end
|
@@ -337,23 +404,37 @@ module LogSense
|
|
337
404
|
ins_error.execute(value)
|
338
405
|
end
|
339
406
|
|
407
|
+
# DO NOT persist the pending_jobs which have not yet completed (those
|
408
|
+
# still available at: pending_jobs).
|
409
|
+
#
|
410
|
+
# In fact various entries initiated with RUNNING end up with "performed"
|
411
|
+
# (rather than COMPLETED). Notice that entries COMPLETED always have a
|
412
|
+
# peformed entry as well.
|
413
|
+
#
|
414
|
+
# Since we do not yet process "performed" entry log and in pending jobs
|
415
|
+
# we end up accumulating a bunch of entries which are marked as
|
416
|
+
# "performed"
|
417
|
+
#
|
418
|
+
# Performed entries are tricky since they use JOB_ID, rather than the
|
419
|
+
# object_id and probably requires to change how we enter pending_ids
|
420
|
+
|
340
421
|
db
|
341
422
|
end
|
342
423
|
|
343
424
|
# could be private here, I guess we keep them public to make them simpler
|
344
425
|
# to try from irb
|
345
426
|
|
346
|
-
TIMESTAMP =
|
347
|
-
LOG_ID =
|
348
|
-
VERB =
|
349
|
-
URL =
|
350
|
-
IP =
|
351
|
-
STATUS =
|
352
|
-
STATUS_IN_WORDS =
|
353
|
-
MSECS =
|
427
|
+
TIMESTAMP = '(?<timestamp>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)'
|
428
|
+
LOG_ID = '(?<log_id>[a-z0-9-]+)'
|
429
|
+
VERB = '(?<verb>GET|POST|PATCH|PUT|DELETE)'
|
430
|
+
URL = '(?<url>[^"]+)'
|
431
|
+
IP = '(?<ip>[0-9.]+)'
|
432
|
+
STATUS = '(?<status>[0-9]+)'
|
433
|
+
STATUS_IN_WORDS = '(OK|Unauthorized|Found|Internal Server Error|Bad Request|Method Not Allowed|Request Timeout|Not Implemented|Bad Gateway|Service Unavailable)'
|
434
|
+
MSECS = '[0-9.]+'
|
354
435
|
|
355
436
|
# 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/
|
437
|
+
STARTED_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] Started #{VERB} "#{URL}" for #{IP} at/o
|
357
438
|
|
358
439
|
def match_and_process_start(line)
|
359
440
|
matchdata = STARTED_REGEXP.match line
|
@@ -372,7 +453,7 @@ module LogSense
|
|
372
453
|
# 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
454
|
# 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
455
|
# 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]+))?\)/
|
456
|
+
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
457
|
|
377
458
|
def match_and_process_completed(line)
|
378
459
|
matchdata = (COMPLETED_REGEXP.match line)
|
@@ -393,7 +474,7 @@ module LogSense
|
|
393
474
|
end
|
394
475
|
|
395
476
|
# 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/
|
477
|
+
PROCESSING_REGEXP = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] Processing by (?<controller>[^ ]+) as/o
|
397
478
|
|
398
479
|
def match_and_process_processing_by line
|
399
480
|
matchdata = PROCESSING_REGEXP.match line
|
@@ -410,7 +491,7 @@ module LogSense
|
|
410
491
|
# F, [2021-12-04T00:34:05.839209 #2735058] FATAL -- : [3a16162e-a6a5-435e-a9d8-c4df5dc0f728]
|
411
492
|
# 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
493
|
|
413
|
-
FATAL_REGEXP = /F, \[#{TIMESTAMP} #[0-9]+\] FATAL -- : \[#{LOG_ID}\]/
|
494
|
+
FATAL_REGEXP = /F, \[#{TIMESTAMP} #[0-9]+\] FATAL -- : \[#{LOG_ID}\]/o
|
414
495
|
|
415
496
|
def match_and_process_fatal(line)
|
416
497
|
matchdata = FATAL_REGEXP.match line
|
@@ -440,8 +521,8 @@ module LogSense
|
|
440
521
|
# [f57e3648-568a-48f9-ae3a-a522b1ff3298] app/models/donations/donation.rb:440:in `build_items_for_delivery'
|
441
522
|
# [f57e3648-568a-48f9-ae3a-a522b1ff3298] app/controllers/donations_controller.rb:1395:in `create_delivery'
|
442
523
|
|
443
|
-
EXCEPTION =
|
444
|
-
FATAL_EXPLANATION_REGEXP = /^\[#{LOG_ID}\] (?<context>#{EXCEPTION})?(?<description>.*)/
|
524
|
+
EXCEPTION = "[A-Za-z_0-9:]+(Error|NotFound|Invalid|Unknown|Missing|ENOSPC)"
|
525
|
+
FATAL_EXPLANATION_REGEXP = /^\[#{LOG_ID}\] (?<context>#{EXCEPTION})?(?<description>.*)/o
|
445
526
|
def match_and_process_fatal_explanation(line)
|
446
527
|
matchdata = FATAL_EXPLANATION_REGEXP.match line
|
447
528
|
if matchdata
|
@@ -454,7 +535,7 @@ module LogSense
|
|
454
535
|
end
|
455
536
|
|
456
537
|
# 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>.+)"/
|
538
|
+
BROWSER_INFO_REGEXP = /BrowserInfo: "(?<browser>.+)","(?<platform>.+)","(?<device_name>.+)","(?<controller>.+)","(?<method>.+)","(?<request_format>.+)","(?<anon_ip>.+)","(?<timestamp>.+)"/o
|
458
539
|
|
459
540
|
def match_and_process_browser_info(line)
|
460
541
|
matchdata = BROWSER_INFO_REGEXP.match line
|
@@ -474,12 +555,14 @@ module LogSense
|
|
474
555
|
|
475
556
|
# Sequence:
|
476
557
|
#
|
477
|
-
# - enqueued
|
478
|
-
# - running
|
479
|
-
# - performing
|
558
|
+
# - enqueued (LOG_ID user event, JOB_ID assigned by system)
|
559
|
+
# - running (JOB_ID links to enqueued; PID assigned by system; OBJECT_ID assigned by the system)
|
560
|
+
# - performing (OBJECT_ID links to running; OBJECT_ID assigned by the system; JOB_ID is new)
|
480
561
|
# - (rendering)
|
481
|
-
# - performed
|
482
|
-
# - completed
|
562
|
+
# - performed (OBJECT_ID links to running; JOB_ID links to previous)
|
563
|
+
# - completed (OBJECT_ID links to running; JOB_ID links to previous)
|
564
|
+
#
|
565
|
+
# SOMETIMES PERFORMED APPEARS WITH NO COMPLETED.
|
483
566
|
#
|
484
567
|
# 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
568
|
#
|
@@ -489,37 +572,99 @@ module LogSense
|
|
489
572
|
#
|
490
573
|
# 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
574
|
#
|
575
|
+
#
|
576
|
+
# 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
|
577
|
+
#
|
578
|
+
#
|
492
579
|
# 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
580
|
|
494
581
|
# Sequence with errors:
|
495
582
|
# (two log entries per error)
|
496
583
|
#
|
497
584
|
# 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.
|
585
|
+
|
498
586
|
# 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
587
|
|
500
|
-
TIMESTAMP_WITH_TZONE =
|
501
|
-
ID =
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
588
|
+
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]+)'
|
589
|
+
ID = '(?<id>[0-9]+)'
|
590
|
+
JOB_ID = '(?<job_id>[a-zA-Z0-9-]+)'
|
591
|
+
WORKER = 'Worker\\((?<worker>.+) host:(?<host>.+) pid:(?<pid>[0-9]+)\\)'
|
592
|
+
METHOD = '(?<method>[A-Za-z0-9:#_]+)'
|
593
|
+
TIMES = '(?<attempt>[0-9]+)'
|
594
|
+
ERROR_MSG = '(?<error_msg>.+)'
|
595
|
+
ARGUMENTS = '(?<arguments>.+)'
|
507
596
|
|
508
|
-
|
597
|
+
#
|
598
|
+
# these are together, since they return temporary data
|
599
|
+
#
|
600
|
+
ENQUEUEING = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : \[#{LOG_ID}\] \[ActiveJob\] Enqueued #{METHOD} \(Job ID: #{JOB_ID}\) to .* with arguments: #{ARGUMENTS}/o
|
509
601
|
|
510
|
-
|
602
|
+
def match_and_process_enqueuing_job(line)
|
603
|
+
matchdata = ENQUEUEING.match line
|
604
|
+
if matchdata
|
605
|
+
{
|
606
|
+
log_id: matchdata[:log_id],
|
607
|
+
job_id: matchdata[:job_id]
|
608
|
+
}
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
RUNNING_MESSAGE = /I, \[#{TIMESTAMP} #[0-9]+\] INFO -- : #{TIMESTAMP_WITH_TZONE}: \[#{WORKER}\] Job #{METHOD} \[#{JOB_ID}\] from .+ with arguments: \[#{ARGUMENTS}\] \(id=#{ID}\) \(queue=.*\) RUNNING/o
|
613
|
+
|
614
|
+
def match_and_process_running_job(line)
|
615
|
+
matchdata = RUNNING_MESSAGE.match line
|
616
|
+
if matchdata
|
617
|
+
{
|
618
|
+
started_at: matchdata[:timestamp],
|
619
|
+
job_id: matchdata[:job_id],
|
620
|
+
object_id: matchdata[:id],
|
621
|
+
pid: matchdata[:pid]
|
622
|
+
}
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
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
|
627
|
+
|
628
|
+
def match_and_process_completed_job(line)
|
629
|
+
matchdata = COMPLETED_MESSAGE.match line
|
630
|
+
if matchdata
|
631
|
+
{
|
632
|
+
ended_at: matchdata[:timestamp],
|
633
|
+
duration_total_ms: matchdata[:duration_total_ms],
|
634
|
+
id: matchdata[:id],
|
635
|
+
job_id: matchdata[:job_id],
|
636
|
+
worker: matchdata[:worker],
|
637
|
+
host: matchdata[:host],
|
638
|
+
pid: matchdata[:pid],
|
639
|
+
log_id: matchdata.named_captures["log_id"],
|
640
|
+
object_id: matchdata[:id],
|
641
|
+
method: matchdata[:method],
|
642
|
+
exit_status: 'C',
|
643
|
+
arguments: matchdata[:arguments]
|
644
|
+
}
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
# similar to completed with I->E, INFO->ERROR, COMPLETED->FAILED and final message structure a bit different
|
649
|
+
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
|
650
|
+
|
651
|
+
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
|
652
|
+
|
653
|
+
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
654
|
|
512
655
|
def match_and_process_job_error(line)
|
513
|
-
[ERROR_MESSAGE, ERROR_MESSAGE_SHORT].map do |regexp|
|
656
|
+
[ERROR_MESSAGE_PERMANENT, ERROR_MESSAGE, ERROR_MESSAGE_SHORT].map do |regexp|
|
514
657
|
matchdata = regexp.match line
|
515
658
|
if matchdata
|
659
|
+
exit_status = matchdata[:error_msg].include? "permanently" ? "F" : "E"
|
660
|
+
|
516
661
|
return {
|
517
662
|
ended_at: matchdata[:timestamp],
|
518
|
-
duration_total_ms:
|
663
|
+
duration_total_ms: nil, # we could compute the time to failure
|
519
664
|
worker: matchdata[:worker],
|
520
665
|
host: matchdata[:host],
|
521
666
|
pid: matchdata[:pid],
|
522
|
-
|
667
|
+
job_id: matchdata.named_captures["job_id"],
|
523
668
|
object_id: matchdata[:id],
|
524
669
|
method: matchdata[:method],
|
525
670
|
arguments: matchdata.named_captures["arguments"],
|
@@ -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 == 'C', 1, 0)) as Completed,
|
140
|
+
sum(iif(exit_status == 'E', 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,
|
@@ -160,13 +164,14 @@ module LogSense
|
|
160
164
|
SELECT worker,
|
161
165
|
host,
|
162
166
|
pid,
|
167
|
+
exit_status,
|
163
168
|
object_id,
|
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 == 'E'
|
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 left right 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
|
281
|
+
header: %w[Worker Host PID ID Error Method Arguments Attempts],
|
259
282
|
column_alignment: %i[left left left left left left left right],
|
260
283
|
column_width: ["5%", "5%", "5%", "5%", "20%", "30%", "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.
|
4
|
+
version: 2.2.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: 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
|