瀏覽代碼

Add --tail flag as option on logs.
Closes #265
Signed-off-by: Stéphane Seguin <[email protected]>

Stéphane Seguin 9 年之前
父節點
當前提交
9b36dc5c54

+ 8 - 5
compose/cli/log_printer.py

@@ -19,13 +19,16 @@ class LogPrinter(object):
                  monochrome=False,
                  monochrome=False,
                  cascade_stop=False,
                  cascade_stop=False,
                  follow=False,
                  follow=False,
-                 timestamps=False):
+                 timestamps=False,
+                 tail="all"):
+
         self.containers = containers
         self.containers = containers
         self.output = utils.get_output_stream(output)
         self.output = utils.get_output_stream(output)
         self.monochrome = monochrome
         self.monochrome = monochrome
         self.cascade_stop = cascade_stop
         self.cascade_stop = cascade_stop
         self.follow = follow
         self.follow = follow
         self.timestamps = timestamps
         self.timestamps = timestamps
+        self.tail = tail
 
 
     def run(self):
     def run(self):
         if not self.containers:
         if not self.containers:
@@ -49,7 +52,7 @@ class LogPrinter(object):
         for color_func, container in zip(color_funcs, self.containers):
         for color_func, container in zip(color_funcs, self.containers):
             generator_func = get_log_generator(container)
             generator_func = get_log_generator(container)
             prefix = color_func(build_log_prefix(container, prefix_width))
             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):
 def build_log_prefix(container, prefix_width):
@@ -72,7 +75,7 @@ def get_log_generator(container):
     return build_no_log_generator
     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
     """Return a generator that prints a warning about logs and waits for
     container to exit.
     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))
         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
     # if the container doesn't have a log_stream we need to attach to container
     # before log printer starts running
     # before log printer starts running
     if container.log_stream is None:
     if container.log_stream is None:
         stream = container.logs(stdout=True, stderr=True, stream=True,
         stream = container.logs(stdout=True, stderr=True, stream=True,
-                                follow=follow, timestamps=timestamps)
+                                follow=follow, timestamps=timestamps, tail=tail)
         line_generator = split_buffer(stream)
         line_generator = split_buffer(stream)
     else:
     else:
         line_generator = split_buffer(container.log_stream)
         line_generator = split_buffer(container.log_stream)

+ 11 - 3
compose/cli/main.py

@@ -329,16 +329,24 @@ class TopLevelCommand(DocoptCommand):
 
 
         Options:
         Options:
             --no-color          Produce monochrome output.
             --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)
         containers = project.containers(service_names=options['SERVICE'], stopped=True)
 
 
         monochrome = options['--no-color']
         monochrome = options['--no-color']
         follow = options['--follow']
         follow = options['--follow']
         timestamps = options['--timestamps']
         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))
         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):
     def pause(self, project, options):
         """
         """

+ 2 - 0
docs/reference/logs.md

@@ -18,6 +18,8 @@ Options:
 --no-color          Produce monochrome output.
 --no-color          Produce monochrome output.
 -f, --follow        Follow log output
 -f, --follow        Follow log output
 -t, --timestamps    Show timestamps
 -t, --timestamps    Show timestamps
+--tail              Number of lines to show from the end of the logs
+                    for each container.
 ```
 ```
 
 
 Displays log output from services.
 Displays log output from services.

+ 8 - 0
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})')
         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):
     def test_kill(self):
         self.dispatch(['up', '-d'], None)
         self.dispatch(['up', '-d'], None)
         service = self.project.get_service('simple')
         service = self.project.get_service('simple')

+ 3 - 0
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"

+ 1 - 1
tests/unit/cli/log_printer_test.py

@@ -47,7 +47,7 @@ class TestLogPrinter(object):
         # Call count is 2 lines + "container exited line"
         # Call count is 2 lines + "container exited line"
         assert output_stream.flush.call_count == 3
         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()
         LogPrinter([mock_container], output=output_stream, follow=False).run()
 
 
         output = output_stream.getvalue()
         output = output_stream.getvalue()