@@ -486,24 +486,20 @@ def test_logs_exported_once_batch_size_reached(self):
486
486
exporter .export .assert_called_once ()
487
487
after_export = time .time_ns ()
488
488
# Shows the worker's 30 second sleep was interrupted within a second.
489
- self .assertTrue (( after_export - before_export ) < 1e9 )
489
+ self .assertLess ( after_export - before_export , 1e9 )
490
490
491
491
# pylint: disable=no-self-use
492
492
def test_logs_exported_once_schedule_delay_reached (self ):
493
493
exporter = Mock ()
494
494
log_record_processor = BatchLogRecordProcessor (
495
495
exporter = exporter ,
496
- # Should not reach this during the test, instead export should be called when delay millis is hit.
497
496
max_queue_size = 15 ,
498
497
max_export_batch_size = 15 ,
499
498
schedule_delay_millis = 100 ,
500
499
)
501
- for _ in range (15 ):
502
- log_record_processor .emit (EMPTY_LOG )
503
- time .sleep (0.11 )
504
- exporter .export .assert_has_calls (
505
- [call ([EMPTY_LOG ]) for _ in range (15 )]
506
- )
500
+ log_record_processor .emit (EMPTY_LOG )
501
+ time .sleep (0.2 )
502
+ exporter .export .assert_called_once_with ([EMPTY_LOG ])
507
503
508
504
def test_logs_flushed_before_shutdown_and_dropped_after_shutdown (self ):
509
505
exporter = Mock ()
@@ -517,15 +513,16 @@ def test_logs_flushed_before_shutdown_and_dropped_after_shutdown(self):
517
513
# This log should be flushed because it was written before shutdown.
518
514
log_record_processor .emit (EMPTY_LOG )
519
515
log_record_processor .shutdown ()
516
+ exporter .export .assert_called_once_with ([EMPTY_LOG ])
520
517
self .assertTrue (exporter ._stopped )
521
518
522
- with self .assertLogs (level = "WARNING " ) as log :
519
+ with self .assertLogs (level = "INFO " ) as log :
523
520
# This log should not be flushed.
524
521
log_record_processor .emit (EMPTY_LOG )
525
522
self .assertEqual (len (log .output ), 1 )
526
523
self .assertEqual (len (log .records ), 1 )
527
524
self .assertIn ("Shutdown called, ignoring log." , log .output [0 ])
528
- exporter .export .assert_called_once_with ([ EMPTY_LOG ] )
525
+ exporter .export .assert_called_once ( )
529
526
530
527
# pylint: disable=no-self-use
531
528
def test_force_flush_flushes_logs (self ):
@@ -554,6 +551,7 @@ def bulk_log_and_flush(num_logs):
554
551
with ThreadPoolExecutor (max_workers = 69 ) as executor :
555
552
for idx in range (69 ):
556
553
executor .submit (bulk_log_and_flush , idx + 1 )
554
+
557
555
executor .shutdown ()
558
556
559
557
finished_logs = exporter .get_finished_logs ()
@@ -563,21 +561,53 @@ def bulk_log_and_flush(num_logs):
563
561
hasattr (os , "fork" ),
564
562
"needs *nix" ,
565
563
)
566
- def test_batch_log_record_processor_fork (self ):
564
+ def test_batch_log_record_processor_fork_clears_logs_from_child (self ):
567
565
exporter = InMemoryLogExporter ()
568
566
log_record_processor = BatchLogRecordProcessor (
569
567
exporter ,
570
568
max_export_batch_size = 64 ,
571
569
schedule_delay_millis = 30000 ,
572
570
)
573
- # These are not expected to be flushed. Calling fork clears any logs not flushed.
571
+ # These logs should be flushed only from the parent process.
572
+ # _at_fork_reinit should be called in the child process, to
573
+ # clear these logs in the child process.
574
574
for _ in range (10 ):
575
575
log_record_processor .emit (EMPTY_LOG )
576
+
577
+ # The below test also needs this, but it can only be set once.
576
578
multiprocessing .set_start_method ("fork" )
577
579
578
580
def child (conn ):
579
- for _ in range (100 ):
581
+ log_record_processor .force_flush ()
582
+ logs = exporter .get_finished_logs ()
583
+ conn .send (len (logs ) == 0 )
584
+ conn .close ()
585
+
586
+ parent_conn , child_conn = multiprocessing .Pipe ()
587
+ process = multiprocessing .Process (target = child , args = (child_conn ,))
588
+ process .start ()
589
+ self .assertTrue (parent_conn .recv ())
590
+ process .join ()
591
+ log_record_processor .force_flush ()
592
+ self .assertTrue (len (exporter .get_finished_logs ()) == 10 )
593
+
594
+ @unittest .skipUnless (
595
+ hasattr (os , "fork" ),
596
+ "needs *nix" ,
597
+ )
598
+ def test_batch_log_record_processor_fork_doesnot_deadlock (self ):
599
+ exporter = InMemoryLogExporter ()
600
+ log_record_processor = BatchLogRecordProcessor (
601
+ exporter ,
602
+ max_export_batch_size = 64 ,
603
+ schedule_delay_millis = 30000 ,
604
+ )
605
+
606
+ def child (conn ):
607
+ def _target ():
580
608
log_record_processor .emit (EMPTY_LOG )
609
+
610
+ ConcurrencyTestBase .run_with_many_threads (_target , 100 )
581
611
log_record_processor .force_flush ()
582
612
logs = exporter .get_finished_logs ()
583
613
conn .send (len (logs ) == 100 )
@@ -588,7 +618,6 @@ def child(conn):
588
618
process .start ()
589
619
self .assertTrue (parent_conn .recv ())
590
620
process .join ()
591
- self .assertTrue (len (exporter .get_finished_logs ()) == 0 )
592
621
593
622
def test_batch_log_record_processor_gc (self ):
594
623
# Given a BatchLogRecordProcessor
@@ -650,4 +679,5 @@ def formatter(record): # pylint: disable=unused-argument
650
679
mock_stdout = Mock ()
651
680
exporter = ConsoleLogExporter (out = mock_stdout , formatter = formatter )
652
681
exporter .export ([EMPTY_LOG ])
682
+
653
683
mock_stdout .write .assert_called_once_with (mock_record_str )
0 commit comments