log_printer.py 2.6 KB

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