diff --git a/compose/config/config.py b/compose/config/config.py index 61b40589e..0012aefd7 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -51,8 +51,6 @@ DOCKER_CONFIG_KEYS = [ 'ipc', 'labels', 'links', - 'log_driver', - 'log_opt', 'mac_address', 'mem_limit', 'memswap_limit', @@ -78,6 +76,7 @@ ALLOWED_KEYS = DOCKER_CONFIG_KEYS + [ 'dockerfile', 'expose', 'external_links', + 'logging', ] DOCKER_VALID_URL_PREFIXES = ( diff --git a/compose/config/fields_schema_v1.json b/compose/config/fields_schema_v1.json index 6f0a36319..790ace349 100644 --- a/compose/config/fields_schema_v1.json +++ b/compose/config/fields_schema_v1.json @@ -75,8 +75,15 @@ "labels": {"$ref": "#/definitions/list_or_dict"}, "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, - "log_driver": {"type": "string"}, - "log_opt": {"type": "object"}, + "logging": { + "type": "object", + + "properties": { + "driver": {"type": "string"}, + "options": {"type": "object"} + }, + "additionalProperties": false + }, "mac_address": {"type": "string"}, "mem_limit": {"type": ["number", "string"]}, diff --git a/compose/service.py b/compose/service.py index 24fa63942..366833dda 100644 --- a/compose/service.py +++ b/compose/service.py @@ -510,6 +510,13 @@ class Service(object): return volumes_from + def get_logging_options(self): + logging_dict = self.options.get('logging', {}) + return { + 'log_driver': logging_dict.get('driver', ""), + 'log_opt': logging_dict.get('options', None) + } + def _get_container_create_options( self, override_options, @@ -523,6 +530,8 @@ class Service(object): for k in DOCKER_CONFIG_KEYS if k in self.options) container_options.update(override_options) + container_options.update(self.get_logging_options()) + if self.custom_container_name() and not one_off: container_options['name'] = self.custom_container_name() elif not container_options.get('name'): @@ -590,10 +599,9 @@ class Service(object): def _get_container_host_config(self, override_options, one_off=False): options = dict(self.options, **override_options) - log_config = LogConfig( - type=options.get('log_driver', ""), - config=options.get('log_opt', None) - ) + logging_dict = options.get('logging', None) + log_config = get_log_config(logging_dict) + return self.client.create_host_config( links=self._get_links(link_to_self=one_off), port_bindings=build_port_bindings(options.get('ports') or []), @@ -953,3 +961,12 @@ def build_ulimits(ulimit_config): ulimits.append(ulimit_dict) return ulimits + + +def get_log_config(logging_dict): + log_driver = logging_dict.get('driver', "") if logging_dict else "" + log_options = logging_dict.get('options', None) if logging_dict else None + return LogConfig( + type=log_driver, + config=log_options + ) diff --git a/docs/compose-file.md b/docs/compose-file.md index 29e0c647c..40a3cf023 100644 --- a/docs/compose-file.md +++ b/docs/compose-file.md @@ -324,29 +324,37 @@ for this service, e.g: Environment variables will also be created - see the [environment variable reference](env.md) for details. -### log_driver +### logging -Specify a logging driver for the service's containers, as with the ``--log-driver`` -option for docker run ([documented here](https://docs.docker.com/engine/reference/logging/overview/)). +Logging configuration for the service. This configuration replaces the previous +`log_driver` and `log_opt` keys. + + logging: + driver: log_driver + options: + syslog-address: "tcp://192.168.0.42:123" + +The `driver` name specifies a logging driver for the service's +containers, as with the ``--log-driver`` option for docker run +([documented here](https://docs.docker.com/engine/reference/logging/overview/)). The default value is json-file. - log_driver: "json-file" - log_driver: "syslog" - log_driver: "none" + driver: "json-file" + driver: "syslog" + driver: "none" > **Note:** Only the `json-file` driver makes the logs available directly from > `docker-compose up` and `docker-compose logs`. Using any other driver will not > print any logs. -### log_opt +Specify logging options for the logging driver with the ``options`` key, as with the ``--log-opt`` option for `docker run`. -Specify logging options with `log_opt` for the logging driver, as with the ``--log-opt`` option for `docker run`. Logging options are key value pairs. An example of `syslog` options: - log_driver: "syslog" - log_opt: + driver: "syslog" + options: syslog-address: "tcp://192.168.0.42:123" ### net diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 6859c7741..c5df30793 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -716,6 +716,43 @@ class CLITestCase(DockerClientTestCase): result = self.dispatch(['start'], returncode=1) assert 'No containers to start' in result.stderr + def test_up_logging(self): + self.base_dir = 'tests/fixtures/logging-composefile' + self.dispatch(['up', '-d']) + simple = self.project.get_service('simple').containers()[0] + log_config = simple.get('HostConfig.LogConfig') + self.assertTrue(log_config) + self.assertEqual(log_config.get('Type'), 'none') + + another = self.project.get_service('another').containers()[0] + log_config = another.get('HostConfig.LogConfig') + self.assertTrue(log_config) + self.assertEqual(log_config.get('Type'), 'json-file') + self.assertEqual(log_config.get('Config')['max-size'], '10m') + + def test_up_logging_with_multiple_files(self): + self.base_dir = 'tests/fixtures/logging-composefile' + config_paths = [ + 'docker-compose.yml', + 'compose2.yml', + ] + self._project = get_project(self.base_dir, config_paths) + self.dispatch( + [ + '-f', config_paths[0], + '-f', config_paths[1], + 'up', '-d', + ], + None) + + containers = self.project.containers() + self.assertEqual(len(containers), 2) + + another = self.project.get_service('another').containers()[0] + log_config = another.get('HostConfig.LogConfig') + self.assertTrue(log_config) + self.assertEqual(log_config.get('Type'), 'none') + def test_pause_unpause(self): self.dispatch(['up', '-d'], None) service = self.project.get_service('simple') diff --git a/tests/fixtures/logging-composefile/compose2.yml b/tests/fixtures/logging-composefile/compose2.yml new file mode 100644 index 000000000..ba5829691 --- /dev/null +++ b/tests/fixtures/logging-composefile/compose2.yml @@ -0,0 +1,3 @@ +another: + logging: + driver: "none" diff --git a/tests/fixtures/logging-composefile/docker-compose.yml b/tests/fixtures/logging-composefile/docker-compose.yml new file mode 100644 index 000000000..877ee5e21 --- /dev/null +++ b/tests/fixtures/logging-composefile/docker-compose.yml @@ -0,0 +1,12 @@ +simple: + image: busybox:latest + command: top + logging: + driver: "none" +another: + image: busybox:latest + command: top + logging: + driver: "json-file" + options: + max-size: "10m" diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py index 4a0eaacb4..86bc4d9db 100644 --- a/tests/integration/service_test.py +++ b/tests/integration/service_test.py @@ -888,7 +888,7 @@ class ServiceTest(DockerClientTestCase): self.assertNotEqual(one_off_container.name, 'my-web-container') def test_log_drive_invalid(self): - service = self.create_service('web', log_driver='xxx') + service = self.create_service('web', logging={'driver': 'xxx'}) expected_error_msg = "logger: no log driver named 'xxx' is registered" with self.assertRaisesRegexp(APIError, expected_error_msg): @@ -902,7 +902,7 @@ class ServiceTest(DockerClientTestCase): self.assertFalse(log_config['Config']) def test_log_drive_none(self): - service = self.create_service('web', log_driver='none') + service = self.create_service('web', logging={'driver': 'none'}) log_config = create_and_start_container(service).log_config self.assertEqual('none', log_config['Type']) diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py index 87d6af595..9cc35c7b2 100644 --- a/tests/unit/service_test.py +++ b/tests/unit/service_test.py @@ -156,7 +156,8 @@ class ServiceTest(unittest.TestCase): self.mock_client.create_host_config.return_value = {} log_opt = {'syslog-address': 'tcp://192.168.0.42:123'} - service = Service(name='foo', image='foo', hostname='name', client=self.mock_client, log_driver='syslog', log_opt=log_opt) + logging = {'driver': 'syslog', 'options': log_opt} + service = Service(name='foo', image='foo', hostname='name', client=self.mock_client, logging=logging) service._get_container_create_options({'some': 'overrides'}, 1) self.assertTrue(self.mock_client.create_host_config.called)