diff --git a/compose/cli/log_printer.py b/compose/cli/log_printer.py index adef19ed..6a8553c5 100644 --- a/compose/cli/log_printer.py +++ b/compose/cli/log_printer.py @@ -19,13 +19,16 @@ class LogPrinter(object): monochrome=False, cascade_stop=False, follow=False, - timestamps=False): + timestamps=False, + tail="all"): + self.containers = containers self.output = utils.get_output_stream(output) self.monochrome = monochrome self.cascade_stop = cascade_stop self.follow = follow self.timestamps = timestamps + self.tail = tail def run(self): if not self.containers: @@ -49,7 +52,7 @@ class LogPrinter(object): for color_func, container in zip(color_funcs, self.containers): generator_func = get_log_generator(container) prefix = color_func(build_log_prefix(container, prefix_width)) - yield generator_func(container, prefix, color_func, self.follow, self.timestamps) + yield generator_func(container, prefix, color_func, self.follow, self.timestamps, self.tail) def build_log_prefix(container, prefix_width): @@ -72,7 +75,7 @@ def get_log_generator(container): return build_no_log_generator -def build_no_log_generator(container, prefix, color_func, follow, timestamps): +def build_no_log_generator(container, prefix, color_func, follow, timestamps, tail): """Return a generator that prints a warning about logs and waits for container to exit. """ @@ -83,12 +86,12 @@ def build_no_log_generator(container, prefix, color_func, follow, timestamps): yield color_func(wait_on_exit(container)) -def build_log_generator(container, prefix, color_func, follow, timestamps): +def build_log_generator(container, prefix, color_func, follow, timestamps, tail): # if the container doesn't have a log_stream we need to attach to container # before log printer starts running if container.log_stream is None: stream = container.logs(stdout=True, stderr=True, stream=True, - follow=follow, timestamps=timestamps) + follow=follow, timestamps=timestamps, tail=tail) line_generator = split_buffer(stream) else: line_generator = split_buffer(container.log_stream) diff --git a/compose/cli/main.py b/compose/cli/main.py index fb309ac5..8cbdce01 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -329,16 +329,24 @@ class TopLevelCommand(DocoptCommand): Options: --no-color Produce monochrome output. - -f, --follow Follow log output - -t, --timestamps Show timestamps + -f, --follow Follow log output. + -t, --timestamps Show timestamps. + --tail="all" Number of lines to show from the end of the logs + for each container. """ containers = project.containers(service_names=options['SERVICE'], stopped=True) monochrome = options['--no-color'] follow = options['--follow'] timestamps = options['--timestamps'] + tail = options['--tail'] + if tail is not None: + if tail.isdigit(): + tail = int(tail) + elif tail != 'all': + raise UserError("tail flag must be all or a number") print("Attaching to", list_containers(containers)) - LogPrinter(containers, monochrome=monochrome, follow=follow, timestamps=timestamps).run() + LogPrinter(containers, monochrome=monochrome, follow=follow, timestamps=timestamps, tail=tail).run() def pause(self, project, options): """ diff --git a/docs/reference/logs.md b/docs/reference/logs.md index 8135f4c9..745d24f7 100644 --- a/docs/reference/logs.md +++ b/docs/reference/logs.md @@ -18,6 +18,8 @@ Options: --no-color Produce monochrome output. -f, --follow Follow log output -t, --timestamps Show timestamps +--tail Number of lines to show from the end of the logs + for each container. ``` Displays log output from services. diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 78e17e44..8e743075 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -1171,6 +1171,14 @@ class CLITestCase(DockerClientTestCase): self.assertRegexpMatches(result.stdout, '(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})') + def test_logs_tail(self): + self.base_dir = 'tests/fixtures/logs-tail-composefile' + self.dispatch(['up'], None) + + result = self.dispatch(['logs', '--tail', '2'], None) + + assert result.stdout.count('\n') == 3 + def test_kill(self): self.dispatch(['up', '-d'], None) service = self.project.get_service('simple') diff --git a/tests/fixtures/logs-tail-composefile/docker-compose.yml b/tests/fixtures/logs-tail-composefile/docker-compose.yml new file mode 100644 index 00000000..80d8feae --- /dev/null +++ b/tests/fixtures/logs-tail-composefile/docker-compose.yml @@ -0,0 +1,3 @@ +simple: + image: busybox:latest + command: sh -c "echo a && echo b && echo c && echo d" diff --git a/tests/unit/cli/log_printer_test.py b/tests/unit/cli/log_printer_test.py index bed0eae8..d5593639 100644 --- a/tests/unit/cli/log_printer_test.py +++ b/tests/unit/cli/log_printer_test.py @@ -47,7 +47,7 @@ class TestLogPrinter(object): # Call count is 2 lines + "container exited line" assert output_stream.flush.call_count == 3 - def test_single_container_without_follow(self, output_stream, mock_container): + def test_single_container_without_stream(self, output_stream, mock_container): LogPrinter([mock_container], output=output_stream, follow=False).run() output = output_stream.getvalue()