log_printer.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. from __future__ import absolute_import
  2. from __future__ import unicode_literals
  3. import sys
  4. from itertools import cycle
  5. import six
  6. from . import colors
  7. from .multiplexer import Multiplexer
  8. from .utils import split_buffer
  9. class LogPrinter(object):
  10. def __init__(self, containers, attach_params=None, output=sys.stdout, monochrome=False):
  11. self.containers = containers
  12. self.attach_params = attach_params or {}
  13. self.prefix_width = self._calculate_prefix_width(containers)
  14. self.generators = self._make_log_generators(monochrome)
  15. self.output = output
  16. def run(self):
  17. mux = Multiplexer(self.generators)
  18. for line in mux.loop():
  19. if isinstance(line, six.text_type) and not six.PY3:
  20. line = line.encode('utf-8')
  21. self.output.write(line)
  22. def _calculate_prefix_width(self, containers):
  23. """
  24. Calculate the maximum width of container names so we can make the log
  25. prefixes line up like so:
  26. db_1 | Listening
  27. web_1 | Listening
  28. """
  29. prefix_width = 0
  30. for container in containers:
  31. prefix_width = max(prefix_width, len(container.name_without_project))
  32. return prefix_width
  33. def _make_log_generators(self, monochrome):
  34. color_fns = cycle(colors.rainbow())
  35. generators = []
  36. def no_color(text):
  37. return text
  38. for container in self.containers:
  39. if monochrome:
  40. color_fn = no_color
  41. else:
  42. color_fn = next(color_fns)
  43. generators.append(self._make_log_generator(container, color_fn))
  44. return generators
  45. def _make_log_generator(self, container, color_fn):
  46. prefix = color_fn(self._generate_prefix(container))
  47. # Attach to container before log printer starts running
  48. line_generator = split_buffer(self._attach(container), '\n')
  49. for line in line_generator:
  50. yield prefix + line
  51. exit_code = container.wait()
  52. yield color_fn("%s exited with code %s\n" % (container.name, exit_code))
  53. def _generate_prefix(self, container):
  54. """
  55. Generate the prefix for a log line without colour
  56. """
  57. name = container.name_without_project
  58. padding = ' ' * (self.prefix_width - len(name))
  59. return ''.join([name, padding, ' | '])
  60. def _attach(self, container):
  61. params = {
  62. 'stdout': True,
  63. 'stderr': True,
  64. 'stream': True,
  65. }
  66. params.update(self.attach_params)
  67. params = dict((name, 1 if value else 0) for (name, value) in list(params.items()))
  68. return container.attach(**params)