@@ -315,14 +315,17 @@ def runLinux(self):
315
315
else :
316
316
tempCommand += [self ._createCommandFile (runFrame .command )]
317
317
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
319
322
# pylint: disable=subprocess-popen-preexec-fn
320
323
frameInfo .forkedCommand = subprocess .Popen (tempCommand ,
321
324
env = self .frameEnv ,
322
325
cwd = self .rqCore .machine .getTempPath (),
323
326
stdin = subprocess .PIPE ,
324
- stdout = self . rqlog ,
325
- stderr = self . rqlog ,
327
+ stdout = file_descriptor ,
328
+ stderr = file_descriptor ,
326
329
close_fds = True ,
327
330
preexec_fn = os .setsid )
328
331
finally :
@@ -335,6 +338,8 @@ def runLinux(self):
335
338
self .rqCore .updateRss )
336
339
self .rqCore .updateRssThread .start ()
337
340
341
+ if rqd .rqconstants .RQD_PREPEND_TIMESTAMP :
342
+ pipe_to_file (frameInfo .forkedCommand .stdout , frameInfo .forkedCommand .stderr , self .rqlog )
338
343
returncode = frameInfo .forkedCommand .wait ()
339
344
340
345
# Find exitStatus and exitSignal
@@ -535,7 +540,7 @@ def run(self):
535
540
else :
536
541
raise RuntimeError (err )
537
542
try :
538
- self .rqlog = open (runFrame .log_dir_file , "w" , 1 )
543
+ self .rqlog = open (runFrame .log_dir_file , "w+ " , 1 )
539
544
self .waitForFile (runFrame .log_dir_file )
540
545
# pylint: disable=broad-except
541
546
except Exception as e :
@@ -1161,3 +1166,98 @@ def sendStatusReport(self):
1161
1166
def isWaitingForIdle (self ):
1162
1167
"""Returns whether the host is waiting until idle to take some action."""
1163
1168
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