Skip to content

Instantly share code, notes, and snippets.

@satra
Created April 1, 2011 12:43
Show Gist options
  • Select an option

  • Save satra/898091 to your computer and use it in GitHub Desktop.

Select an option

Save satra/898091 to your computer and use it in GitHub Desktop.
class Stream(object):
"""Function to capture stdout and stderr streams with timestamps
http://stackoverflow.com/questions/4984549/merge-and-sync-stdout-and-stderr/5188359#5188359
"""
def __init__(self, name, impl):
self._name = name
self._impl = impl
self._buf = ''
self._rows = []
self._lastidx = 0
def fileno(self):
"Pass-through for file descriptor."
return self._impl.fileno()
def read(self, drain=0):
"Read from the file descriptor. If 'drain' set, read until EOF."
while self._read(drain) is not None:
if not drain:
break
def _read(self, drain):
"Read from the file descriptor"
fd = self.fileno()
buf = os.read(fd, 4096)
if not buf and not self._buf:
return None
if '\n' not in buf:
if not drain:
self._buf += buf
return []
# prepend any data previously read, then split into lines and format
buf = self._buf + buf
if '\n' in buf:
tmp, rest = buf.rsplit('\n', 1)
else:
tmp = buf
rest = None
self._buf = rest
now = datetime.datetime.now().isoformat()
rows = tmp.split('\n')
self._rows += [(now, '%s %s:%s' % (self._name, now, r), r) for r in rows]
for idx in range(self._lastidx, len(self._rows)):
iflogger.info(self._rows[idx][1])
self._lastidx = len(self._rows)
def run_command(runtime, timeout=0.1):
"""
Run a command, read stdout and stderr, prefix with timestamp. The returned
runtime contains a merged stdout+stderr log with timestamps
http://stackoverflow.com/questions/4984549/merge-and-sync-stdout-and-stderr/5188359#5188359
"""
PIPE = subprocess.PIPE
proc = subprocess.Popen(runtime.cmdline,
stdout=PIPE,
stderr=PIPE,
shell=True,
cwd=runtime.cwd,
env=runtime.environ)
streams = [
Stream('stdout', proc.stdout),
Stream('stderr', proc.stderr)
]
def _process(drain=0):
res = select.select(streams, [], [], timeout)
for stream in res[0]:
stream.read(drain)
while proc.returncode is None:
proc.poll()
_process()
runtime.returncode = proc.returncode
_process(drain=1)
# collect results, merge and return
result = {}
temp = []
for stream in streams:
rows = stream._rows
temp += rows
result[stream._name] = [r[2] for r in rows]
temp.sort()
result['merged'] = [r[1] for r in temp]
runtime.stderr = '\n'.join(result['stderr'])
runtime.stdout = '\n'.join(result['stdout'])
runtime.merged = result['merged']
return runtime
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment