We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Allow to configure uvicorn logging
Trying to add custom handlers to the application logger we have found that the uvicorn ones are not working as expected. It seems that uvicorn overrides the logging configuration when the Config object is initialized.

It would be nice to allow users to configure the uvicorn logger. This can be done by setting the path to a logging configuration file when creating uvicorn's Config object.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mlserver/logging.py`
Content:
```
1 import logging
2 import sys
3 
4 from logging import Formatter, StreamHandler
5 
6 from .settings import Settings
7 
8 LoggerName = "mlserver"
9 LoggerFormat = "%(asctime)s [%(name)s] %(levelname)s - %(message)s"
10 
11 logger = logging.getLogger(LoggerName)
12 
13 
14 def get_logger():
15     return logger
16 
17 
18 def configure_logger(settings: Settings = None):
19     logger = get_logger()
20 
21     # Don't add handler twice
22     if not logger.handlers:
23         stream_handler = StreamHandler(sys.stdout)
24         formatter = Formatter(LoggerFormat)
25         stream_handler.setFormatter(formatter)
26 
27         logger.addHandler(stream_handler)
28 
29     logger.setLevel(logging.INFO)
30     if settings and settings.debug:
31         logger.setLevel(logging.DEBUG)
32 
33     return logger
34
```

Path: `mlserver/rest/server.py`
Content:
```
1 import uvicorn
2 
3 from ..settings import Settings
4 from ..handlers import DataPlane, ModelRepositoryHandlers, get_custom_handlers
5 from ..model import MLModel
6 
7 from .utils import matches
8 from .app import create_app
9 
10 
11 class _NoSignalServer(uvicorn.Server):
12     def install_signal_handlers(self):
13         pass
14 
15 
16 class RESTServer:
17     def __init__(
18         self,
19         settings: Settings,
20         data_plane: DataPlane,
21         model_repository_handlers: ModelRepositoryHandlers,
22     ):
23         self._settings = settings
24         self._data_plane = data_plane
25         self._model_repository_handlers = model_repository_handlers
26         self._app = create_app(
27             self._settings,
28             data_plane=self._data_plane,
29             model_repository_handlers=self._model_repository_handlers,
30         )
31 
32     async def add_custom_handlers(self, model: MLModel):
33         handlers = get_custom_handlers(model)
34         for custom_handler, handler_method in handlers:
35             self._app.add_api_route(
36                 custom_handler.rest_path,
37                 handler_method,
38                 methods=[custom_handler.rest_method],
39             )
40 
41     async def delete_custom_handlers(self, model: MLModel):
42         handlers = get_custom_handlers(model)
43         if len(handlers) == 0:
44             return
45 
46         # NOTE: Loop in reverse, so that it's quicker to find all the recently
47         # added routes and we can remove routes on-the-fly
48         for i, route in reversed(list(enumerate(self._app.routes))):
49             for j, (custom_handler, handler_method) in enumerate(handlers):
50                 if matches(route, custom_handler, handler_method):  # type: ignore
51                     self._app.routes.pop(i)
52                     handlers.pop(j)
53 
54     async def start(self):
55         cfg = uvicorn.Config(
56             self._app, host=self._settings.host, port=self._settings.http_port
57         )
58         self._server = _NoSignalServer(cfg)
59         await self._server.serve()
60 
61     async def stop(self):
62         self._server.handle_exit(sig=None, frame=None)
63
```

Path: `mlserver/settings.py`
Content:
```
1 from typing import List, Optional
2 from pydantic import BaseSettings, PyObject
3 
4 from .version import __version__
5 from .types import MetadataTensor
6 
7 ENV_PREFIX_SETTINGS = "MLSERVER_"
8 ENV_PREFIX_MODEL_SETTINGS = "MLSERVER_MODEL_"
9 
10 
11 class CORSSettings(BaseSettings):
12     class Config:
13         env_prefix = ENV_PREFIX_SETTINGS
14 
15     allow_origins: Optional[List[str]] = []
16     """
17     A list of origins that should be permitted to make
18     cross-origin requests. E.g. ['https://example.org', 'https://www.example.org'].
19     You can use ['*'] to allow any origin
20     """
21 
22     allow_origin_regex: Optional[str] = None
23     """
24     A regex string to match against origins that
25     should be permitted to make cross-origin requests.
26     e.g. 'https:\\/\\/.*\\.example\\.org'
27     """
28 
29     allow_credentials: Optional[bool] = False
30     """Indicate that cookies should be supported for cross-origin requests"""
31 
32     allow_methods: Optional[List[str]] = ["GET"]
33     """A list of HTTP methods that should be allowed for cross-origin requests"""
34 
35     allow_headers: Optional[List[str]] = []
36     """A list of HTTP request headers that should be supported for
37     cross-origin requests"""
38 
39     expose_headers: Optional[List[str]] = []
40     """Indicate any response headers that should be made accessible to the browser"""
41 
42     max_age: Optional[int] = 600
43     """Sets a maximum time in seconds for browsers to cache CORS responses"""
44 
45 
46 class Settings(BaseSettings):
47     class Config:
48         env_prefix = ENV_PREFIX_SETTINGS
49 
50     debug: bool = True
51 
52     # Model repository settings
53     model_repository_root: str = "."
54     """Root of the model repository, where we will search for models."""
55 
56     load_models_at_startup: bool = True
57     """Flag to load all available models automatically at startup."""
58 
59     # Server metadata
60     server_name: str = "mlserver"
61     """Name of the server."""
62 
63     server_version: str = __version__
64     """Version of the server."""
65 
66     extensions: List[str] = []
67     """Server extensions loaded."""
68 
69     # Server settings
70     host: str = "0.0.0.0"
71     """Host where to listen for connections."""
72 
73     http_port: int = 8080
74     """Port where to listen for HTTP / REST connections."""
75 
76     grpc_port: int = 8081
77     """Port where to listen for gRPC connections."""
78 
79     grpc_max_message_length: Optional[int] = None
80     """Maximum length (i.e. size) of gRPC payloads."""
81 
82     # CORS settings
83     cors_settings: Optional[CORSSettings] = None
84 
85     # Metrics settings
86     metrics_endpoint: Optional[str] = "/metrics"
87     """
88     Endpoint used to expose Prometheus metrics. Alternatively, can be set to
89     `None` to disable it
90     """
91 
92 
93 class ModelParameters(BaseSettings):
94     """
95     Parameters that apply only to a particular instance of a model.
96     This can include things like model weights, or arbitrary ``extra``
97     parameters particular to the underlying inference runtime.
98     The main difference with respect to ``ModelSettings`` is that parameters
99     can change on each instance (e.g. each version) of the model.
100     """
101 
102     class Config:
103         env_prefix = ENV_PREFIX_MODEL_SETTINGS
104 
105     uri: Optional[str] = None
106     """
107     URI where the model artifacts can be found.
108     This path must be either absolute or relative to where MLServer is running.
109     """
110 
111     version: Optional[str] = None
112     """Version of the model."""
113 
114     format: Optional[str] = None
115     """Format of the model (only available on certain runtimes)."""
116 
117     content_type: Optional[str] = None
118     """Default content type to use for requests and responses."""
119 
120     extra: Optional[dict] = {}
121     """Arbitrary settings, dependent on the inference runtime
122     implementation."""
123 
124 
125 class ModelSettings(BaseSettings):
126     class Config:
127         env_prefix = ENV_PREFIX_MODEL_SETTINGS
128         underscore_attrs_are_private = True
129 
130     # Source points to the file where model settings were loaded from
131     _source: Optional[str] = None
132 
133     name: str = ""
134     """Name of the model."""
135 
136     # Model metadata
137     platform: str = ""
138     """Framework used to train and serialise the model (e.g. sklearn)."""
139 
140     versions: List[str] = []
141     """Versions of dependencies used to train the model (e.g.
142     sklearn/0.20.1)."""
143 
144     inputs: List[MetadataTensor] = []
145     """Metadata about the inputs accepted by the model."""
146 
147     outputs: List[MetadataTensor] = []
148     """Metadata about the outputs returned by the model."""
149 
150     # Parallel settings
151     parallel_workers: int = 4
152     """When parallel inference is enabled, number of workers to run inference
153     across."""
154 
155     warm_workers: bool = False
156     """When parallel inference is enabled, optionally load model to all workers
157     on startup"""
158 
159     # Adaptive Batching settings (disabled by default)
160     max_batch_size: int = 0
161     """When adaptive batching is enabled, maximum number of requests to group
162     together in a single batch."""
163 
164     max_batch_time: float = 0.0
165     """When adaptive batching is enabled, maximum amount of time (in seconds)
166     to wait for enough requests to build a full batch."""
167 
168     # Custom model class implementation
169     implementation: PyObject = "mlserver.model.MLModel"  # type: ignore
170     """*Python path* to the inference runtime to use to serve this model (e.g.
171     ``mlserver_sklearn.SKLearnModel``)."""
172 
173     # Model parameters are meant to be set directly by the MLServer runtime.
174     # However, it's also possible to override them manually.
175     parameters: Optional[ModelParameters] = None
176     """Extra parameters for each instance of this model."""
177
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mlserver/logging.py b/mlserver/logging.py
--- a/mlserver/logging.py
+++ b/mlserver/logging.py
@@ -2,6 +2,7 @@
 import sys
 
 from logging import Formatter, StreamHandler
+import logging.config
 
 from .settings import Settings
 
@@ -15,6 +16,10 @@
     return logger
 
 
+def apply_logging_file(logging_settings: str):
+    logging.config.fileConfig(fname=logging_settings, disable_existing_loggers=False)
+
+
 def configure_logger(settings: Settings = None):
     logger = get_logger()
 
@@ -30,4 +35,7 @@
     if settings and settings.debug:
         logger.setLevel(logging.DEBUG)
 
+    if settings and settings.logging_settings:
+        apply_logging_file(settings.logging_settings)
+
     return logger
diff --git a/mlserver/rest/server.py b/mlserver/rest/server.py
--- a/mlserver/rest/server.py
+++ b/mlserver/rest/server.py
@@ -53,7 +53,10 @@
 
     async def start(self):
         cfg = uvicorn.Config(
-            self._app, host=self._settings.host, port=self._settings.http_port
+            self._app,
+            host=self._settings.host,
+            port=self._settings.http_port,
+            log_config=self._settings.logging_settings,
         )
         self._server = _NoSignalServer(cfg)
         await self._server.serve()
diff --git a/mlserver/settings.py b/mlserver/settings.py
--- a/mlserver/settings.py
+++ b/mlserver/settings.py
@@ -89,6 +89,10 @@
     `None` to disable it
     """
 
+    # Logging settings
+    logging_settings: Optional[str] = None
+    """Path to logging config file"""
+
 
 class ModelParameters(BaseSettings):
     """

{"golden_diff": "diff --git a/mlserver/logging.py b/mlserver/logging.py\n--- a/mlserver/logging.py\n+++ b/mlserver/logging.py\n@@ -2,6 +2,7 @@\n import sys\n \n from logging import Formatter, StreamHandler\n+import logging.config\n \n from .settings import Settings\n \n@@ -15,6 +16,10 @@\n     return logger\n \n \n+def apply_logging_file(logging_settings: str):\n+    logging.config.fileConfig(fname=logging_settings, disable_existing_loggers=False)\n+\n+\n def configure_logger(settings: Settings = None):\n     logger = get_logger()\n \n@@ -30,4 +35,7 @@\n     if settings and settings.debug:\n         logger.setLevel(logging.DEBUG)\n \n+    if settings and settings.logging_settings:\n+        apply_logging_file(settings.logging_settings)\n+\n     return logger\ndiff --git a/mlserver/rest/server.py b/mlserver/rest/server.py\n--- a/mlserver/rest/server.py\n+++ b/mlserver/rest/server.py\n@@ -53,7 +53,10 @@\n \n     async def start(self):\n         cfg = uvicorn.Config(\n-            self._app, host=self._settings.host, port=self._settings.http_port\n+            self._app,\n+            host=self._settings.host,\n+            port=self._settings.http_port,\n+            log_config=self._settings.logging_settings,\n         )\n         self._server = _NoSignalServer(cfg)\n         await self._server.serve()\ndiff --git a/mlserver/settings.py b/mlserver/settings.py\n--- a/mlserver/settings.py\n+++ b/mlserver/settings.py\n@@ -89,6 +89,10 @@\n     `None` to disable it\n     \"\"\"\n \n+    # Logging settings\n+    logging_settings: Optional[str] = None\n+    \"\"\"Path to logging config file\"\"\"\n+\n \n class ModelParameters(BaseSettings):\n     \"\"\"\n", "issue": "Allow to configure uvicorn logging\nTrying to add custom handlers to the application logger we have found that the uvicorn ones are not working as expected. It seems that uvicorn overrides the logging configuration when the Config object is initialized.\r\n\r\nIt would be nice to allow users to configure the uvicorn logger. This can be done by setting the path to a logging configuration file when creating uvicorn's Config object.\n", "before_files": [{"content": "import logging\nimport sys\n\nfrom logging import Formatter, StreamHandler\n\nfrom .settings import Settings\n\nLoggerName = \"mlserver\"\nLoggerFormat = \"%(asctime)s [%(name)s] %(levelname)s - %(message)s\"\n\nlogger = logging.getLogger(LoggerName)\n\n\ndef get_logger():\n    return logger\n\n\ndef configure_logger(settings: Settings = None):\n    logger = get_logger()\n\n    # Don't add handler twice\n    if not logger.handlers:\n        stream_handler = StreamHandler(sys.stdout)\n        formatter = Formatter(LoggerFormat)\n        stream_handler.setFormatter(formatter)\n\n        logger.addHandler(stream_handler)\n\n    logger.setLevel(logging.INFO)\n    if settings and settings.debug:\n        logger.setLevel(logging.DEBUG)\n\n    return logger\n", "path": "mlserver/logging.py"}, {"content": "import uvicorn\n\nfrom ..settings import Settings\nfrom ..handlers import DataPlane, ModelRepositoryHandlers, get_custom_handlers\nfrom ..model import MLModel\n\nfrom .utils import matches\nfrom .app import create_app\n\n\nclass _NoSignalServer(uvicorn.Server):\n    def install_signal_handlers(self):\n        pass\n\n\nclass RESTServer:\n    def __init__(\n        self,\n        settings: Settings,\n        data_plane: DataPlane,\n        model_repository_handlers: ModelRepositoryHandlers,\n    ):\n        self._settings = settings\n        self._data_plane = data_plane\n        self._model_repository_handlers = model_repository_handlers\n        self._app = create_app(\n            self._settings,\n            data_plane=self._data_plane,\n            model_repository_handlers=self._model_repository_handlers,\n        )\n\n    async def add_custom_handlers(self, model: MLModel):\n        handlers = get_custom_handlers(model)\n        for custom_handler, handler_method in handlers:\n            self._app.add_api_route(\n                custom_handler.rest_path,\n                handler_method,\n                methods=[custom_handler.rest_method],\n            )\n\n    async def delete_custom_handlers(self, model: MLModel):\n        handlers = get_custom_handlers(model)\n        if len(handlers) == 0:\n            return\n\n        # NOTE: Loop in reverse, so that it's quicker to find all the recently\n        # added routes and we can remove routes on-the-fly\n        for i, route in reversed(list(enumerate(self._app.routes))):\n            for j, (custom_handler, handler_method) in enumerate(handlers):\n                if matches(route, custom_handler, handler_method):  # type: ignore\n                    self._app.routes.pop(i)\n                    handlers.pop(j)\n\n    async def start(self):\n        cfg = uvicorn.Config(\n            self._app, host=self._settings.host, port=self._settings.http_port\n        )\n        self._server = _NoSignalServer(cfg)\n        await self._server.serve()\n\n    async def stop(self):\n        self._server.handle_exit(sig=None, frame=None)\n", "path": "mlserver/rest/server.py"}, {"content": "from typing import List, Optional\nfrom pydantic import BaseSettings, PyObject\n\nfrom .version import __version__\nfrom .types import MetadataTensor\n\nENV_PREFIX_SETTINGS = \"MLSERVER_\"\nENV_PREFIX_MODEL_SETTINGS = \"MLSERVER_MODEL_\"\n\n\nclass CORSSettings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_SETTINGS\n\n    allow_origins: Optional[List[str]] = []\n    \"\"\"\n    A list of origins that should be permitted to make\n    cross-origin requests. E.g. ['https://example.org', 'https://www.example.org'].\n    You can use ['*'] to allow any origin\n    \"\"\"\n\n    allow_origin_regex: Optional[str] = None\n    \"\"\"\n    A regex string to match against origins that\n    should be permitted to make cross-origin requests.\n    e.g. 'https:\\\\/\\\\/.*\\\\.example\\\\.org'\n    \"\"\"\n\n    allow_credentials: Optional[bool] = False\n    \"\"\"Indicate that cookies should be supported for cross-origin requests\"\"\"\n\n    allow_methods: Optional[List[str]] = [\"GET\"]\n    \"\"\"A list of HTTP methods that should be allowed for cross-origin requests\"\"\"\n\n    allow_headers: Optional[List[str]] = []\n    \"\"\"A list of HTTP request headers that should be supported for\n    cross-origin requests\"\"\"\n\n    expose_headers: Optional[List[str]] = []\n    \"\"\"Indicate any response headers that should be made accessible to the browser\"\"\"\n\n    max_age: Optional[int] = 600\n    \"\"\"Sets a maximum time in seconds for browsers to cache CORS responses\"\"\"\n\n\nclass Settings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_SETTINGS\n\n    debug: bool = True\n\n    # Model repository settings\n    model_repository_root: str = \".\"\n    \"\"\"Root of the model repository, where we will search for models.\"\"\"\n\n    load_models_at_startup: bool = True\n    \"\"\"Flag to load all available models automatically at startup.\"\"\"\n\n    # Server metadata\n    server_name: str = \"mlserver\"\n    \"\"\"Name of the server.\"\"\"\n\n    server_version: str = __version__\n    \"\"\"Version of the server.\"\"\"\n\n    extensions: List[str] = []\n    \"\"\"Server extensions loaded.\"\"\"\n\n    # Server settings\n    host: str = \"0.0.0.0\"\n    \"\"\"Host where to listen for connections.\"\"\"\n\n    http_port: int = 8080\n    \"\"\"Port where to listen for HTTP / REST connections.\"\"\"\n\n    grpc_port: int = 8081\n    \"\"\"Port where to listen for gRPC connections.\"\"\"\n\n    grpc_max_message_length: Optional[int] = None\n    \"\"\"Maximum length (i.e. size) of gRPC payloads.\"\"\"\n\n    # CORS settings\n    cors_settings: Optional[CORSSettings] = None\n\n    # Metrics settings\n    metrics_endpoint: Optional[str] = \"/metrics\"\n    \"\"\"\n    Endpoint used to expose Prometheus metrics. Alternatively, can be set to\n    `None` to disable it\n    \"\"\"\n\n\nclass ModelParameters(BaseSettings):\n    \"\"\"\n    Parameters that apply only to a particular instance of a model.\n    This can include things like model weights, or arbitrary ``extra``\n    parameters particular to the underlying inference runtime.\n    The main difference with respect to ``ModelSettings`` is that parameters\n    can change on each instance (e.g. each version) of the model.\n    \"\"\"\n\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    uri: Optional[str] = None\n    \"\"\"\n    URI where the model artifacts can be found.\n    This path must be either absolute or relative to where MLServer is running.\n    \"\"\"\n\n    version: Optional[str] = None\n    \"\"\"Version of the model.\"\"\"\n\n    format: Optional[str] = None\n    \"\"\"Format of the model (only available on certain runtimes).\"\"\"\n\n    content_type: Optional[str] = None\n    \"\"\"Default content type to use for requests and responses.\"\"\"\n\n    extra: Optional[dict] = {}\n    \"\"\"Arbitrary settings, dependent on the inference runtime\n    implementation.\"\"\"\n\n\nclass ModelSettings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n        underscore_attrs_are_private = True\n\n    # Source points to the file where model settings were loaded from\n    _source: Optional[str] = None\n\n    name: str = \"\"\n    \"\"\"Name of the model.\"\"\"\n\n    # Model metadata\n    platform: str = \"\"\n    \"\"\"Framework used to train and serialise the model (e.g. sklearn).\"\"\"\n\n    versions: List[str] = []\n    \"\"\"Versions of dependencies used to train the model (e.g.\n    sklearn/0.20.1).\"\"\"\n\n    inputs: List[MetadataTensor] = []\n    \"\"\"Metadata about the inputs accepted by the model.\"\"\"\n\n    outputs: List[MetadataTensor] = []\n    \"\"\"Metadata about the outputs returned by the model.\"\"\"\n\n    # Parallel settings\n    parallel_workers: int = 4\n    \"\"\"When parallel inference is enabled, number of workers to run inference\n    across.\"\"\"\n\n    warm_workers: bool = False\n    \"\"\"When parallel inference is enabled, optionally load model to all workers\n    on startup\"\"\"\n\n    # Adaptive Batching settings (disabled by default)\n    max_batch_size: int = 0\n    \"\"\"When adaptive batching is enabled, maximum number of requests to group\n    together in a single batch.\"\"\"\n\n    max_batch_time: float = 0.0\n    \"\"\"When adaptive batching is enabled, maximum amount of time (in seconds)\n    to wait for enough requests to build a full batch.\"\"\"\n\n    # Custom model class implementation\n    implementation: PyObject = \"mlserver.model.MLModel\"  # type: ignore\n    \"\"\"*Python path* to the inference runtime to use to serve this model (e.g.\n    ``mlserver_sklearn.SKLearnModel``).\"\"\"\n\n    # Model parameters are meant to be set directly by the MLServer runtime.\n    # However, it's also possible to override them manually.\n    parameters: Optional[ModelParameters] = None\n    \"\"\"Extra parameters for each instance of this model.\"\"\"\n", "path": "mlserver/settings.py"}], "after_files": [{"content": "import logging\nimport sys\n\nfrom logging import Formatter, StreamHandler\nimport logging.config\n\nfrom .settings import Settings\n\nLoggerName = \"mlserver\"\nLoggerFormat = \"%(asctime)s [%(name)s] %(levelname)s - %(message)s\"\n\nlogger = logging.getLogger(LoggerName)\n\n\ndef get_logger():\n    return logger\n\n\ndef apply_logging_file(logging_settings: str):\n    logging.config.fileConfig(fname=logging_settings, disable_existing_loggers=False)\n\n\ndef configure_logger(settings: Settings = None):\n    logger = get_logger()\n\n    # Don't add handler twice\n    if not logger.handlers:\n        stream_handler = StreamHandler(sys.stdout)\n        formatter = Formatter(LoggerFormat)\n        stream_handler.setFormatter(formatter)\n\n        logger.addHandler(stream_handler)\n\n    logger.setLevel(logging.INFO)\n    if settings and settings.debug:\n        logger.setLevel(logging.DEBUG)\n\n    if settings and settings.logging_settings:\n        apply_logging_file(settings.logging_settings)\n\n    return logger\n", "path": "mlserver/logging.py"}, {"content": "import uvicorn\n\nfrom ..settings import Settings\nfrom ..handlers import DataPlane, ModelRepositoryHandlers, get_custom_handlers\nfrom ..model import MLModel\n\nfrom .utils import matches\nfrom .app import create_app\n\n\nclass _NoSignalServer(uvicorn.Server):\n    def install_signal_handlers(self):\n        pass\n\n\nclass RESTServer:\n    def __init__(\n        self,\n        settings: Settings,\n        data_plane: DataPlane,\n        model_repository_handlers: ModelRepositoryHandlers,\n    ):\n        self._settings = settings\n        self._data_plane = data_plane\n        self._model_repository_handlers = model_repository_handlers\n        self._app = create_app(\n            self._settings,\n            data_plane=self._data_plane,\n            model_repository_handlers=self._model_repository_handlers,\n        )\n\n    async def add_custom_handlers(self, model: MLModel):\n        handlers = get_custom_handlers(model)\n        for custom_handler, handler_method in handlers:\n            self._app.add_api_route(\n                custom_handler.rest_path,\n                handler_method,\n                methods=[custom_handler.rest_method],\n            )\n\n    async def delete_custom_handlers(self, model: MLModel):\n        handlers = get_custom_handlers(model)\n        if len(handlers) == 0:\n            return\n\n        # NOTE: Loop in reverse, so that it's quicker to find all the recently\n        # added routes and we can remove routes on-the-fly\n        for i, route in reversed(list(enumerate(self._app.routes))):\n            for j, (custom_handler, handler_method) in enumerate(handlers):\n                if matches(route, custom_handler, handler_method):  # type: ignore\n                    self._app.routes.pop(i)\n                    handlers.pop(j)\n\n    async def start(self):\n        cfg = uvicorn.Config(\n            self._app,\n            host=self._settings.host,\n            port=self._settings.http_port,\n            log_config=self._settings.logging_settings,\n        )\n        self._server = _NoSignalServer(cfg)\n        await self._server.serve()\n\n    async def stop(self):\n        self._server.handle_exit(sig=None, frame=None)\n", "path": "mlserver/rest/server.py"}, {"content": "from typing import List, Optional\nfrom pydantic import BaseSettings, PyObject\n\nfrom .version import __version__\nfrom .types import MetadataTensor\n\nENV_PREFIX_SETTINGS = \"MLSERVER_\"\nENV_PREFIX_MODEL_SETTINGS = \"MLSERVER_MODEL_\"\n\n\nclass CORSSettings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_SETTINGS\n\n    allow_origins: Optional[List[str]] = []\n    \"\"\"\n    A list of origins that should be permitted to make\n    cross-origin requests. E.g. ['https://example.org', 'https://www.example.org'].\n    You can use ['*'] to allow any origin\n    \"\"\"\n\n    allow_origin_regex: Optional[str] = None\n    \"\"\"\n    A regex string to match against origins that\n    should be permitted to make cross-origin requests.\n    e.g. 'https:\\\\/\\\\/.*\\\\.example\\\\.org'\n    \"\"\"\n\n    allow_credentials: Optional[bool] = False\n    \"\"\"Indicate that cookies should be supported for cross-origin requests\"\"\"\n\n    allow_methods: Optional[List[str]] = [\"GET\"]\n    \"\"\"A list of HTTP methods that should be allowed for cross-origin requests\"\"\"\n\n    allow_headers: Optional[List[str]] = []\n    \"\"\"A list of HTTP request headers that should be supported for\n    cross-origin requests\"\"\"\n\n    expose_headers: Optional[List[str]] = []\n    \"\"\"Indicate any response headers that should be made accessible to the browser\"\"\"\n\n    max_age: Optional[int] = 600\n    \"\"\"Sets a maximum time in seconds for browsers to cache CORS responses\"\"\"\n\n\nclass Settings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_SETTINGS\n\n    debug: bool = True\n\n    # Model repository settings\n    model_repository_root: str = \".\"\n    \"\"\"Root of the model repository, where we will search for models.\"\"\"\n\n    load_models_at_startup: bool = True\n    \"\"\"Flag to load all available models automatically at startup.\"\"\"\n\n    # Server metadata\n    server_name: str = \"mlserver\"\n    \"\"\"Name of the server.\"\"\"\n\n    server_version: str = __version__\n    \"\"\"Version of the server.\"\"\"\n\n    extensions: List[str] = []\n    \"\"\"Server extensions loaded.\"\"\"\n\n    # Server settings\n    host: str = \"0.0.0.0\"\n    \"\"\"Host where to listen for connections.\"\"\"\n\n    http_port: int = 8080\n    \"\"\"Port where to listen for HTTP / REST connections.\"\"\"\n\n    grpc_port: int = 8081\n    \"\"\"Port where to listen for gRPC connections.\"\"\"\n\n    grpc_max_message_length: Optional[int] = None\n    \"\"\"Maximum length (i.e. size) of gRPC payloads.\"\"\"\n\n    # CORS settings\n    cors_settings: Optional[CORSSettings] = None\n\n    # Metrics settings\n    metrics_endpoint: Optional[str] = \"/metrics\"\n    \"\"\"\n    Endpoint used to expose Prometheus metrics. Alternatively, can be set to\n    `None` to disable it\n    \"\"\"\n\n    # Logging settings\n    logging_settings: Optional[str] = None\n    \"\"\"Path to logging config file\"\"\"\n\n\nclass ModelParameters(BaseSettings):\n    \"\"\"\n    Parameters that apply only to a particular instance of a model.\n    This can include things like model weights, or arbitrary ``extra``\n    parameters particular to the underlying inference runtime.\n    The main difference with respect to ``ModelSettings`` is that parameters\n    can change on each instance (e.g. each version) of the model.\n    \"\"\"\n\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n\n    uri: Optional[str] = None\n    \"\"\"\n    URI where the model artifacts can be found.\n    This path must be either absolute or relative to where MLServer is running.\n    \"\"\"\n\n    version: Optional[str] = None\n    \"\"\"Version of the model.\"\"\"\n\n    format: Optional[str] = None\n    \"\"\"Format of the model (only available on certain runtimes).\"\"\"\n\n    content_type: Optional[str] = None\n    \"\"\"Default content type to use for requests and responses.\"\"\"\n\n    extra: Optional[dict] = {}\n    \"\"\"Arbitrary settings, dependent on the inference runtime\n    implementation.\"\"\"\n\n\nclass ModelSettings(BaseSettings):\n    class Config:\n        env_prefix = ENV_PREFIX_MODEL_SETTINGS\n        underscore_attrs_are_private = True\n\n    # Source points to the file where model settings were loaded from\n    _source: Optional[str] = None\n\n    name: str = \"\"\n    \"\"\"Name of the model.\"\"\"\n\n    # Model metadata\n    platform: str = \"\"\n    \"\"\"Framework used to train and serialise the model (e.g. sklearn).\"\"\"\n\n    versions: List[str] = []\n    \"\"\"Versions of dependencies used to train the model (e.g.\n    sklearn/0.20.1).\"\"\"\n\n    inputs: List[MetadataTensor] = []\n    \"\"\"Metadata about the inputs accepted by the model.\"\"\"\n\n    outputs: List[MetadataTensor] = []\n    \"\"\"Metadata about the outputs returned by the model.\"\"\"\n\n    # Parallel settings\n    parallel_workers: int = 4\n    \"\"\"When parallel inference is enabled, number of workers to run inference\n    across.\"\"\"\n\n    warm_workers: bool = False\n    \"\"\"When parallel inference is enabled, optionally load model to all workers\n    on startup\"\"\"\n\n    # Adaptive Batching settings (disabled by default)\n    max_batch_size: int = 0\n    \"\"\"When adaptive batching is enabled, maximum number of requests to group\n    together in a single batch.\"\"\"\n\n    max_batch_time: float = 0.0\n    \"\"\"When adaptive batching is enabled, maximum amount of time (in seconds)\n    to wait for enough requests to build a full batch.\"\"\"\n\n    # Custom model class implementation\n    implementation: PyObject = \"mlserver.model.MLModel\"  # type: ignore\n    \"\"\"*Python path* to the inference runtime to use to serve this model (e.g.\n    ``mlserver_sklearn.SKLearnModel``).\"\"\"\n\n    # Model parameters are meant to be set directly by the MLServer runtime.\n    # However, it's also possible to override them manually.\n    parameters: Optional[ModelParameters] = None\n    \"\"\"Extra parameters for each instance of this model.\"\"\"\n", "path": "mlserver/settings.py"}]}