Skip to content

Commit a5b8c26

Browse files
Refactor logging and error messages in serverless (feast-dev#1923)
* Add versioning to Docker image name Signed-off-by: Felix Wang <[email protected]> * Add more details to error messages Signed-off-by: Felix Wang <[email protected]> * Change versioning Signed-off-by: Felix Wang <[email protected]> * Switch from print statements to logger Signed-off-by: Felix Wang <[email protected]> * Change logging settings to be controlled through command line for Feast CLI Signed-off-by: Felix Wang <[email protected]> * Remove versioning changes for Docker image name Signed-off-by: Felix Wang <[email protected]> * Update logging Signed-off-by: Felix Wang <[email protected]> * Enforce default logging level at the argument level Signed-off-by: Felix Wang <[email protected]>
1 parent 7f35793 commit a5b8c26

File tree

4 files changed

+47
-34
lines changed

4 files changed

+47
-34
lines changed

sdk/python/feast/cli.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import logging
1616
from datetime import datetime
1717
from pathlib import Path
18-
from typing import List
18+
from typing import List, Optional
1919

2020
import click
2121
import pkg_resources
@@ -57,8 +57,13 @@ def format_options(self, ctx: click.Context, formatter: click.HelpFormatter):
5757
"-c",
5858
help="Switch to a different feature repository directory before executing the given subcommand.",
5959
)
60+
@click.option(
61+
"--log-level",
62+
default="info",
63+
help="The logging level. One of DEBUG, INFO, WARNING, ERROR, and CRITICAL (case-insensitive).",
64+
)
6065
@click.pass_context
61-
def cli(ctx: click.Context, chdir: str):
66+
def cli(ctx: click.Context, chdir: Optional[str], log_level: str):
6267
"""
6368
Feast CLI
6469
@@ -68,6 +73,15 @@ def cli(ctx: click.Context, chdir: str):
6873
"""
6974
ctx.ensure_object(dict)
7075
ctx.obj["CHDIR"] = Path.cwd() if chdir is None else Path(chdir).absolute()
76+
try:
77+
level = getattr(logging, log_level.upper())
78+
logging.basicConfig(
79+
format="%(asctime)s %(levelname)s:%(message)s",
80+
datefmt="%m/%d/%Y %I:%M:%S %p",
81+
level=level,
82+
)
83+
except Exception as e:
84+
raise e
7185
pass
7286

7387

sdk/python/feast/errors.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,14 @@ def __init__(self):
282282

283283

284284
class AwsLambdaDoesNotExist(Exception):
285-
def __init__(self):
286-
super().__init__("The created AWS Lambda function does not exist.")
285+
def __init__(self, resource_name: str):
286+
super().__init__(
287+
f"The AWS Lambda function {resource_name} should have been created properly, but does not exist."
288+
)
287289

288290

289291
class AwsAPIGatewayDoesNotExist(Exception):
290-
def __init__(self):
291-
super().__init__("The created AWS API Gateway does not exist.")
292+
def __init__(self, resource_name: str):
293+
super().__init__(
294+
f"The AWS API Gateway {resource_name} should have been created properly, but does not exist."
295+
)

sdk/python/feast/infra/aws.py

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import base64
2+
import logging
23
import os
34
import uuid
45
from datetime import datetime
@@ -9,7 +10,6 @@
910

1011
from colorama import Fore, Style
1112

12-
from feast import __version__
1313
from feast.constants import (
1414
AWS_LAMBDA_FEATURE_SERVER_IMAGE,
1515
FEAST_USAGE,
@@ -30,6 +30,7 @@
3030
from feast.protos.feast.core.Registry_pb2 import Registry as RegistryProto
3131
from feast.registry_store import RegistryStore
3232
from feast.repo_config import RegistryConfig
33+
from feast.version import get_version
3334

3435
try:
3536
import boto3
@@ -38,11 +39,10 @@
3839

3940
raise FeastExtrasDependencyImportError("aws", str(e))
4041

42+
_logger = logging.getLogger(__name__)
4143

42-
class AwsProvider(PassthroughProvider):
43-
def _get_lambda_name(self, project: str):
44-
return f"feast-python-server-{project}-{__version__.replace('+', '_').replace('.', '_')}"
4544

45+
class AwsProvider(PassthroughProvider):
4646
def update_infra(
4747
self,
4848
project: str,
@@ -63,9 +63,8 @@ def update_infra(
6363

6464
if self.repo_config.feature_server and self.repo_config.feature_server.enabled:
6565
image_uri = self._upload_docker_image(project)
66-
print("Deploying feature server...")
66+
_logger.info("Deploying feature server...")
6767

68-
assert self.repo_config.repo_path
6968
if not self.repo_config.repo_path:
7069
raise RepoConfigPathDoesNotExist()
7170
with open(self.repo_config.repo_path / "feature_store.yaml", "rb") as f:
@@ -79,7 +78,7 @@ def update_infra(
7978

8079
if function is None:
8180
# If the Lambda function does not exist, create it.
82-
print(" Creating AWS Lambda...")
81+
_logger.info(" Creating AWS Lambda...")
8382
lambda_client.create_function(
8483
FunctionName=resource_name,
8584
Role=self.repo_config.feature_server.execution_role_name,
@@ -95,14 +94,12 @@ def update_infra(
9594
Tags={
9695
"feast-owned": "True",
9796
"project": project,
98-
"feast-sdk-version": __version__.replace("+", "_").replace(
99-
".", "_"
100-
),
97+
"feast-sdk-version": get_version(),
10198
},
10299
)
103100
function = aws_utils.get_lambda_function(lambda_client, resource_name)
104101
if not function:
105-
raise AwsLambdaDoesNotExist()
102+
raise AwsLambdaDoesNotExist(resource_name)
106103
else:
107104
# If the feature_store.yaml has changed, need to update the environment variable.
108105
env = function.get("Environment", {}).get("Variables", {})
@@ -111,7 +108,7 @@ def update_infra(
111108
# It's expected that feature_store.yaml is not regularly updated while the lambda
112109
# is serving production traffic. However, the update in registry (e.g. modifying
113110
# feature views, feature services, and other definitions does not update lambda).
114-
print(" Updating AWS Lambda...")
111+
_logger.info(" Updating AWS Lambda...")
115112

116113
lambda_client.update_function_configuration(
117114
FunctionName=resource_name,
@@ -123,7 +120,7 @@ def update_infra(
123120
api = aws_utils.get_first_api_gateway(api_gateway_client, resource_name)
124121
if not api:
125122
# If the API Gateway doesn't exist, create it
126-
print(" Creating AWS API Gateway...")
123+
_logger.info(" Creating AWS API Gateway...")
127124
api = api_gateway_client.create_api(
128125
Name=resource_name,
129126
ProtocolType="HTTP",
@@ -132,13 +129,11 @@ def update_infra(
132129
Tags={
133130
"feast-owned": "True",
134131
"project": project,
135-
"feast-sdk-version": __version__.replace("+", "_").replace(
136-
".", "_"
137-
),
132+
"feast-sdk-version": get_version(),
138133
},
139134
)
140135
if not api:
141-
raise AwsAPIGatewayDoesNotExist()
136+
raise AwsAPIGatewayDoesNotExist(resource_name)
142137
# Make sure to give AWS Lambda a permission to be invoked by the newly created API Gateway
143138
api_id = api["ApiId"]
144139
region = lambda_client.meta.region_name
@@ -163,20 +158,20 @@ def teardown_infra(
163158
self.repo_config.feature_server is not None
164159
and self.repo_config.feature_server.enabled
165160
):
166-
print("Tearing down feature server...")
161+
_logger.info("Tearing down feature server...")
167162
resource_name = self._get_lambda_name(project)
168163
lambda_client = boto3.client("lambda")
169164
api_gateway_client = boto3.client("apigatewayv2")
170165

171166
function = aws_utils.get_lambda_function(lambda_client, resource_name)
172167

173168
if function is not None:
174-
print(" Tearing down AWS Lambda...")
169+
_logger.info(" Tearing down AWS Lambda...")
175170
aws_utils.delete_lambda_function(lambda_client, resource_name)
176171

177172
api = aws_utils.get_first_api_gateway(api_gateway_client, resource_name)
178173
if api is not None:
179-
print(" Tearing down AWS API Gateway...")
174+
_logger.info(" Tearing down AWS API Gateway...")
180175
aws_utils.delete_api_gateway(api_gateway_client, api["ApiId"])
181176

182177
def _upload_docker_image(self, project: str) -> str:
@@ -213,16 +208,16 @@ def _upload_docker_image(self, project: str) -> str:
213208

214209
raise DockerDaemonNotRunning()
215210

216-
print(
211+
_logger.info(
217212
f"Pulling remote image {Style.BRIGHT + Fore.GREEN}{AWS_LAMBDA_FEATURE_SERVER_IMAGE}{Style.RESET_ALL}:"
218213
)
219214
docker_client.images.pull(AWS_LAMBDA_FEATURE_SERVER_IMAGE)
220215

221-
version = __version__.replace("+", "_").replace(".", "_")
216+
version = get_version()
222217
repository_name = f"feast-python-server-{project}-{version}"
223218
ecr_client = boto3.client("ecr")
224219
try:
225-
print(
220+
_logger.info(
226221
f"Creating remote ECR repository {Style.BRIGHT + Fore.GREEN}{repository_name}{Style.RESET_ALL}:"
227222
)
228223
response = ecr_client.create_repository(repositoryName=repository_name)
@@ -243,13 +238,16 @@ def _upload_docker_image(self, project: str) -> str:
243238

244239
image = docker_client.images.get(AWS_LAMBDA_FEATURE_SERVER_IMAGE)
245240
image_remote_name = f"{repository_uri}:{version}"
246-
print(
241+
_logger.info(
247242
f"Pushing local image to remote {Style.BRIGHT + Fore.GREEN}{image_remote_name}{Style.RESET_ALL}:"
248243
)
249244
image.tag(image_remote_name)
250245
docker_client.api.push(repository_uri, tag=version)
251246
return image_remote_name
252247

248+
def _get_lambda_name(self, project: str):
249+
return f"feast-python-server-{project}-{get_version()}"
250+
253251

254252
class S3RegistryStore(RegistryStore):
255253
def __init__(self, registry_config: RegistryConfig, repo_path: Path):

sdk/python/feast/version.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33

44
def get_version():
5-
"""
6-
Returns version information of the Feast Python Package
7-
"""
8-
5+
"""Returns version information of the Feast Python Package."""
96
try:
107
sdk_version = pkg_resources.get_distribution("feast").version
118
except pkg_resources.DistributionNotFound:

0 commit comments

Comments
 (0)