diff --git a/docker/api/container.py b/docker/api/container.py index 94889e97..764a0ec6 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -1,5 +1,6 @@ import six import warnings +from datetime import datetime from .. import errors from .. import utils @@ -163,7 +164,7 @@ class ContainerApiMixin(object): @utils.check_resource def logs(self, container, stdout=True, stderr=True, stream=False, - timestamps=False, tail='all'): + timestamps=False, tail='all', since=None): if utils.compare_version('1.11', self._version) >= 0: params = {'stderr': stderr and 1 or 0, 'stdout': stdout and 1 or 0, @@ -174,6 +175,17 @@ class ContainerApiMixin(object): if tail != 'all' and (not isinstance(tail, int) or tail <= 0): tail = 'all' params['tail'] = tail + + if since is not None: + if utils.compare_version('1.19', self._version) < 0: + raise errors.InvalidVersion( + 'since is not supported in API < 1.19' + ) + else: + if isinstance(since, datetime): + params['since'] = utils.datetime_to_timestamp(since) + elif (isinstance(since, int) and since > 0): + params['since'] = since url = self._url("/containers/{0}/logs", container) res = self._get(url, params=params, stream=stream) return self._get_result(container, stream, res) diff --git a/docker/utils/__init__.py b/docker/utils/__init__.py index fd0ef5c0..58e5877a 100644 --- a/docker/utils/__init__.py +++ b/docker/utils/__init__.py @@ -1,7 +1,7 @@ from .utils import ( compare_version, convert_port_bindings, convert_volume_binds, mkbuildcontext, tar, exclude_paths, parse_repository_tag, parse_host, - kwargs_from_env, convert_filters, create_host_config, + kwargs_from_env, convert_filters, datetime_to_timestamp, create_host_config, create_container_config, parse_bytes, ping_registry, parse_env_file, version_lt, version_gte ) # flake8: noqa diff --git a/docs/api.md b/docs/api.md index 690fe495..1b8b1ac4 100644 --- a/docs/api.md +++ b/docs/api.md @@ -325,6 +325,7 @@ Sets up an exec instance in a running container. * cmd (str or list): Command to be executed * stdout (bool): Attach to stdout of the exec command if true. Default: True * stderr (bool): Attach to stderr of the exec command if true. Default: True +* since (UTC datetime or int): Output logs from this timestamp. Default: `None` (all logs are given) * tty (bool): Allocate a pseudo-TTY. Default: False * user (str): User to execute command as. Default: root diff --git a/tests/test.py b/tests/test.py index 719ac9ef..83a05097 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1439,6 +1439,7 @@ class DockerClientTest(Cleanup, base.BaseTestCase): ) def test_log_tail(self): + with mock.patch('docker.Client.inspect_container', fake_inspect_container): self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=False, @@ -1453,6 +1454,39 @@ class DockerClientTest(Cleanup, base.BaseTestCase): stream=False ) + def test_log_since(self): + ts = 809222400 + with mock.patch('docker.Client.inspect_container', + fake_inspect_container): + self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=False, + since=ts) + + fake_request.assert_called_with( + 'GET', + url_prefix + 'containers/3cc2351ab11b/logs', + params={'timestamps': 0, 'follow': 0, 'stderr': 1, 'stdout': 1, + 'tail': 'all', 'since': ts}, + timeout=DEFAULT_TIMEOUT_SECONDS, + stream=False + ) + + def test_log_since_with_datetime(self): + ts = 809222400 + time = datetime.datetime.utcfromtimestamp(ts) + with mock.patch('docker.Client.inspect_container', + fake_inspect_container): + self.client.logs(fake_api.FAKE_CONTAINER_ID, stream=False, + since=time) + + fake_request.assert_called_with( + 'GET', + url_prefix + 'containers/3cc2351ab11b/logs', + params={'timestamps': 0, 'follow': 0, 'stderr': 1, 'stdout': 1, + 'tail': 'all', 'since': ts}, + timeout=DEFAULT_TIMEOUT_SECONDS, + stream=False + ) + def test_log_tty(self): m = mock.Mock() with mock.patch('docker.Client.inspect_container',