log_sense 2.1.0 → 2.2.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.
- 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
|