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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e17413654dc552a716548d7a55e127c5f0703cf29643d97d500e0cf51b20eba3
4
- data.tar.gz: 56765b54fdda4f64add3fbb97ea3b4073976d25f4cb67cc932a7d350d85793cb
3
+ metadata.gz: 8b24aa444f64beeb8b13bda22aae730130cff025b4d122557499d3afd2ec2e73
4
+ data.tar.gz: 809c695be701f9a6f17a4b31d4c9c61827a97a9088cb180b6f7f60a794e498f3
5
5
  SHA512:
6
- metadata.gz: dc77b1c63c08a9a190aae0ab0ab44bdffe37e0dc2e999d3dc8a236703dc8e430fb32ff09d884d5d9e90ff7870b7bb212d0cc486e877982ca3c5328a3aeda5943
7
- data.tar.gz: e73ad267ad54dc4e3c09481ec4f055e6d9e1e9346262b04975b754bd8822b19ce27298299ce6fb7cc3511e5248b6303392c984b48d68d40bd398bd14d7be9d80
6
+ metadata.gz: afff457f8f952f7f6b806baaf56ac34010027498be04fdbfaa91653ab70699f3d3d85222b47437c2df151f00fb953e23d21a5ac537562cf14a157459762e4855
7
+ data.tar.gz: 911594383266801861454d6facb9ee4c2db852979d2349250d988934721925ed6592b9fdbb2c63d43814305ca062478d8984bd07166ff90c3928120dc17c3129
data/CHANGELOG.org CHANGED
@@ -2,6 +2,11 @@
2
2
  #+AUTHOR: Adolfo Villafiorita
3
3
  #+STARTUP: showall
4
4
 
5
+ * 2.2.0
6
+
7
+ - [User] Better management of Delayed Job (show also completed)
8
+ - [Refactoring] Regexps are now pre-compiled with /o
9
+
5
10
  * 2.1.0
6
11
 
7
12
  - [User] Delayed Job Errors
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- log_sense (2.0.0)
4
+ log_sense (2.2.0)
5
5
  browser (~> 5.3.0)
6
6
  ipaddr (~> 1.2.0)
7
7
  iso_country_codes (~> 0.7.0)
data/exe/log_sense CHANGED
@@ -168,4 +168,6 @@ else
168
168
 
169
169
  warn "Emitting..." if @options[:verbose]
170
170
  puts LogSense::Emitter.emit @reports, @data, @options
171
+
172
+ exit 0
171
173
  end
@@ -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[:ended_at], # this is temporary (while we wait to parse BEGIN) + required by filter
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 = /(?<timestamp>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+)/
347
- LOG_ID = /(?<log_id>[a-z0-9-]+)/
348
- VERB = /(?<verb>GET|POST|PATCH|PUT|DELETE)/
349
- URL = /(?<url>[^"]+)/
350
- IP = /(?<ip>[0-9.]+)/
351
- STATUS = /(?<status>[0-9]+)/
352
- STATUS_IN_WORDS = /(OK|Unauthorized|Found|Internal Server Error|Bad Request|Method Not Allowed|Request Timeout|Not Implemented|Bad Gateway|Service Unavailable)/
353
- MSECS = /[0-9.]+/
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 = /[A-Za-z_0-9:]+(Error|NotFound|Invalid|Unknown|Missing|ENOSPC)/
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 = /(?<timestamp_tzone>[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\+[0-9]+)/
501
- ID = /(?<id>[0-9]+)/
502
- WORKER = /Worker\((?<worker>.*) host:(?<host>.+) pid:(?<pid>[0-9]+)\)/
503
- METHOD = /(?<method>[A-Za-z0-9:#]+)/
504
- TIMES = /(?<attempt>[0-9]+)/
505
- ERROR_MSG = /(?<error_msg>.+)/
506
- ARGUMENTS = /(?<arguments>.+)/
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
- ERROR_MESSAGE = /E, \[#{TIMESTAMP} #[0-9]+\] ERROR -- : #{TIMESTAMP_WITH_TZONE}: \[#{WORKER}\] Job #{METHOD} \[#{LOG_ID}\] from .+ with arguments: \[#{ARGUMENTS}\] \(id=#{ID}\) \(queue=.*\) FAILED \(#{TIMES} prior attempts\) with #{ERROR_MSG}/
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
- ERROR_MESSAGE_SHORT = /E, \[#{TIMESTAMP} #[0-9]+\] ERROR -- : #{TIMESTAMP_WITH_TZONE}: \[#{WORKER}\] Job #{METHOD} \(id=#{ID}\) FAILED \(#{TIMES} prior attempts\) with #{ERROR_MSG}/
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: 0,
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
- log_id: matchdata.named_captures["log_id"],
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
- @job_error_plot = @db.execute %(
137
+ @job_plot = @db.execute %(
138
138
  SELECT strftime("%Y-%m-%d", ended_at) as Day,
139
- count(distinct(id)) as Errors
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
- @job_error = @db.execute %(
146
- SELECT strftime("%Y-%m-%d %H:%M", ended_at),
147
- worker,
148
- host,
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: '#DEDEDE',
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: "Job Error",
214
- header: %w[Date Worker Host PID ID Error Method Arguments Attempt],
215
- column_alignment: %i[left left left left left left left left right],
216
- column_width: ["10%", "5%", "5%", "5%", "5%", "20%", "20%", "20%", "10%"],
217
- rows: data[:job_error],
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[:job_error_plot].to_json}",
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: 'Errors',
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 Attempt],
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 < 100 ? hash[ip][date].uniq.join(WORDS_SEPARATOR) : "[too many]"
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
@@ -1,3 +1,3 @@
1
1
  module LogSense
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end
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 Sidebar foreground color in new apache report
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.0
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-20 00:00:00.000000000 Z
11
+ date: 2024-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: browser