log_printer.py 2.4 KB

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