@@ -315,14 +315,17 @@ def runLinux(self):
315315 else :
316316 tempCommand += [self ._createCommandFile (runFrame .command )]
317317
318- # Actual cwd is set by /shots/SHOW/home/perl/etc/qwrap.cuerun
318+ if rqd .rqconstants .RQD_PREPEND_TIMESTAMP :
319+ file_descriptor = subprocess .PIPE
320+ else :
321+ file_descriptor = self .rqlog
319322 # pylint: disable=subprocess-popen-preexec-fn
320323 frameInfo .forkedCommand = subprocess .Popen (tempCommand ,
321324 env = self .frameEnv ,
322325 cwd = self .rqCore .machine .getTempPath (),
323326 stdin = subprocess .PIPE ,
324- stdout = self . rqlog ,
325- stderr = self . rqlog ,
327+ stdout = file_descriptor ,
328+ stderr = file_descriptor ,
326329 close_fds = True ,
327330 preexec_fn = os .setsid )
328331 finally :
@@ -335,6 +338,8 @@ def runLinux(self):
335338 self .rqCore .updateRss )
336339 self .rqCore .updateRssThread .start ()
337340
341+ if rqd .rqconstants .RQD_PREPEND_TIMESTAMP :
342+ pipe_to_file (frameInfo .forkedCommand .stdout , frameInfo .forkedCommand .stderr , self .rqlog )
338343 returncode = frameInfo .forkedCommand .wait ()
339344
340345 # Find exitStatus and exitSignal
@@ -535,7 +540,7 @@ def run(self):
535540 else :
536541 raise RuntimeError (err )
537542 try :
538- self .rqlog = open (runFrame .log_dir_file , "w" , 1 )
543+ self .rqlog = open (runFrame .log_dir_file , "w+ " , 1 )
539544 self .waitForFile (runFrame .log_dir_file )
540545 # pylint: disable=broad-except
541546 except Exception as e :
@@ -1161,3 +1166,98 @@ def sendStatusReport(self):
11611166 def isWaitingForIdle (self ):
11621167 """Returns whether the host is waiting until idle to take some action."""
11631168 return self .__whenIdle
1169+
1170+ def pipe_to_file (stdout , stderr , outfile ):
1171+ """
1172+ Prepend entries on stdout and stderr with a timestamp and write to outfile.
1173+
1174+ The logic to poll stdout/stderr is inspired by the Popen.communicate implementation.
1175+ This feature is linux specific
1176+ """
1177+ # Importing packages internally to avoid compatibility issues with Windows
1178+
1179+ if stdout is None or stderr is None :
1180+ return
1181+ outfile .flush ()
1182+ os .fsync (outfile )
1183+
1184+ import select
1185+ import errno
1186+
1187+ fd2file = {}
1188+ fd2output = {}
1189+
1190+ poller = select .poll ()
1191+
1192+ def register_and_append (file_ojb , eventmask ):
1193+ poller .register (file_ojb , eventmask )
1194+ fd2file [file_ojb .fileno ()] = file_ojb
1195+
1196+ def close_and_unregister_and_remove (fd , close = False ):
1197+ poller .unregister (fd )
1198+ if close :
1199+ fd2file [fd ].close ()
1200+ fd2file .pop (fd )
1201+
1202+ def print_and_flush_ln (fd , last_timestamp ):
1203+ txt = '' .join (fd2output [fd ])
1204+ lines = txt .split ('\n ' )
1205+ next_line_timestamp = None
1206+
1207+ # Save the timestamp of the first break
1208+ if last_timestamp is None :
1209+ curr_line_timestamp = datetime .datetime .now ().strftime ("%H:%M:%S" )
1210+ else :
1211+ curr_line_timestamp = last_timestamp
1212+
1213+ # There are no line breaks
1214+ if len (lines ) < 2 :
1215+ return curr_line_timestamp
1216+ else :
1217+ next_line_timestamp = datetime .datetime .now ().strftime ("%H:%M:%S" )
1218+
1219+ remainder = lines [- 1 ]
1220+ for line in lines [0 :- 1 ]:
1221+ print ("[%s] %s" % (curr_line_timestamp , line ), file = outfile )
1222+ outfile .flush ()
1223+ os .fsync (outfile )
1224+ fd2output [fd ] = [remainder ]
1225+
1226+ if next_line_timestamp is None :
1227+ return curr_line_timestamp
1228+ else :
1229+ return next_line_timestamp
1230+
1231+ def translate_newlines (data ):
1232+ data = data .decode ("utf-8" , "ignore" )
1233+ return data .replace ("\r \n " , "\n " ).replace ("\r " , "\n " )
1234+
1235+ select_POLLIN_POLLPRI = select .POLLIN | select .POLLPRI
1236+ # stdout
1237+ register_and_append (stdout , select_POLLIN_POLLPRI )
1238+ fd2output [stdout .fileno ()] = []
1239+
1240+ # stderr
1241+ register_and_append (stderr , select_POLLIN_POLLPRI )
1242+ fd2output [stderr .fileno ()] = []
1243+
1244+ while fd2file :
1245+ try :
1246+ ready = poller .poll ()
1247+ except select .error as e :
1248+ if e .args [0 ] == errno .EINTR :
1249+ continue
1250+ raise
1251+
1252+ first_chunk_timestamp = None
1253+ for fd , mode in ready :
1254+ if mode & select_POLLIN_POLLPRI :
1255+ data = os .read (fd , 4096 )
1256+ if not data :
1257+ close_and_unregister_and_remove (fd )
1258+ if not isinstance (data , str ):
1259+ data = translate_newlines (data )
1260+ fd2output [fd ].append (data )
1261+ first_chunk_timestamp = print_and_flush_ln (fd , first_chunk_timestamp )
1262+ else :
1263+ close_and_unregister_and_remove (fd )
0 commit comments