Skip to content

Instantly share code, notes, and snippets.

@biggers
Forked from pmuller/streamlogger.py
Last active June 30, 2017 14:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save biggers/bda186280e8de94a939f5d8a84fda20c to your computer and use it in GitHub Desktop.
Save biggers/bda186280e8de94a939f5d8a84fda20c to your computer and use it in GitHub Desktop.
StreamLogger is a context handler which proxies an output stream to python's logging
#!/usr/bin/env python3
#
# Port of "Fabric streamlogger" to Python3
# Credit (originally): https://gist.github.com/2376336.git
# pmuller - streamlogger.py
import sys
import logging
from io import StringIO
class StreamLogger(object):
"""
A helper which intercepts what's written to an output stream
then sends it, line by line, to a `logging.Logger` instance.
Usage:
By overwriting `sys.stdout`:
sys.stdout = StreamLogger('stdout')
print 'foo'
As a context manager:
with StreamLogger('stdout'):
print 'foo'
"""
def __init__(self, name, logger=None, unbuffered=False,
flush_on_new_line=True):
"""
``name``: The stream name to incercept ('stdout' or 'stderr')
``logger``: The logger that will receive what's written to the stream.
``unbuffered``: If `True`, `.flush()` will be called each time
`.write()` is called.
``flush_on_new_line``: If `True`, `.flush()` will be called each time
`.write()` is called with data containing a
new line character.
"""
self.__name = name
self.__stream = getattr(sys, name)
self.__logger = logger or logging.getLogger()
self.__buffer = StringIO()
self.__unbuffered = unbuffered
self.__flush_on_new_line = flush_on_new_line
def write(self, data):
"""Write data to the stream.
"""
self.__buffer.write(data)
if self.__unbuffered is True or \
(self.__flush_on_new_line is True and '\n' in data):
self.flush()
def flush(self):
"""Flush the stream.
"""
self.__buffer.seek(0)
while True:
line = self.__buffer.readline()
if line:
if line[-1] == '\n':
line = line[:-1]
if line:
level, line = self.parse(line)
logger = getattr(self.__logger, level)
logger(line)
else:
self.__buffer.seek(0)
self.__buffer.write(line)
self.__buffer.truncate()
break
else:
self.__buffer.seek(0)
self.__buffer.truncate()
break
def parse(self, data):
"""Override me!
"""
return 'info', data
def isatty(self):
"""I'm not a tty.
"""
return False
def __enter__(self):
"""Enter the context manager.
"""
setattr(sys, self.__name, self)
def __exit__(self, exc_type, exc_value, traceback):
"""Leave the context manager.
"""
setattr(sys, self.__name, self.__stream)
def fabric_logging():
"""Example use of `StreamLogger` to intercept Fabric's output.
"""
from fabric.api import run, env, local
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(levelname)s: %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
# Intercepts Fabric's output
env.host_string = 'localhost' if len(sys.argv) < 2 else sys.argv[1]
with StreamLogger('stdout', logger):
if 'localhost' in env.host_string:
local('ls -l')
else:
run('ls -l')
if __name__ == '__main__':
fabric_logging()
"""
>>> fabric_logging()
fabric_logging()
INFO: [localhost] local: ls -l
total 8
-rw-r--r-- 1 mabigger staff 3458 Jun 30 10:04 streamlogger.py
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment