44import logging
55import os
66import re
7+ import threading
78from datetime import datetime
89
910OTF_LOG_FORMAT = (
@@ -393,7 +394,11 @@ def close_log_file(logger__: logging.Logger, result: bool = False) -> None:
393394 # Now everything is closed, we can rename the log file
394395 # If result is True, then rename the file and remove _running from the name
395396 if new_log_filename :
396- os .rename (log_file_name , new_log_filename )
397+ # Use lock to prevent race condition when multiple threads rename same file
398+ with _file_operations_lock :
399+ # Check file still exists before renaming (another thread may have renamed it)
400+ if os .path .exists (log_file_name ):
401+ os .rename (log_file_name , new_log_filename )
397402
398403 # Change the basename of the handler to match the new filename, in case it
399404 # wants to log anything else
@@ -443,6 +448,9 @@ def redact(log_message: str) -> str:
443448
444449logger = init_logging (__name__ )
445450
451+ # Thread lock for directory creation and file operations
452+ _file_operations_lock = threading .Lock ()
453+
446454
447455# mypy: ignore-errors
448456class TaskFileHandler (logging .FileHandler ):
@@ -461,3 +469,22 @@ def __init__(self, filename, mode="a", encoding=None, delay=True):
461469 """
462470 _mkdir (os .path .dirname (filename ))
463471 logging .FileHandler .__init__ (self , filename , mode , encoding , delay )
472+
473+ def _open (self ):
474+ """Open the log file, ensuring the directory exists.
475+
476+ Overrides the FileHandler._open to ensure directory creation is thread-safe
477+ even when delay=True is used. Also uses line buffering for real-time log updates,
478+ which is important when using network filesystems like EFS.
479+ """
480+ # Ensure directory exists with thread safety before opening file
481+ with _file_operations_lock :
482+ _mkdir (os .path .dirname (self .baseFilename ))
483+ # Open with line buffering (buffering=1) for real-time log visibility on network filesystems
484+ return open (
485+ self .baseFilename ,
486+ self .mode ,
487+ buffering = 1 ,
488+ encoding = self .encoding ,
489+ errors = self .errors ,
490+ )
0 commit comments