Skip to content

Commit 6981a78

Browse files
committed
refactor: create LogFetcher to simply assertion
1 parent fc5e8a6 commit 6981a78

File tree

3 files changed

+77
-34
lines changed

3 files changed

+77
-34
lines changed

aws_lambda_powertools/shared/constants.py

+9
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,12 @@
2323
XRAY_SDK_CORE_MODULE: str = "aws_xray_sdk.core"
2424

2525
IDEMPOTENCY_DISABLED_ENV: str = "POWERTOOLS_IDEMPOTENCY_DISABLED"
26+
27+
LOGGER_LAMBDA_CONTEXT_KEYS = [
28+
"function_arn",
29+
"function_memory_size",
30+
"function_name",
31+
"function_request_id",
32+
"cold_start",
33+
"xray_trace_id",
34+
]

tests/e2e/logger/test_logger.py

+7-12
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,17 @@ def basic_handler_fn_arn(infrastructure: dict) -> str:
1818

1919
def test_basic_lambda_logs_visible(basic_handler_fn, basic_handler_fn_arn):
2020
# GIVEN
21-
required_keys = (
22-
"xray_trace_id",
23-
"function_request_id",
24-
"function_arn",
25-
"function_memory_size",
26-
"function_name",
27-
"cold_start",
28-
)
2921
message = "logs should be visible with default settings"
30-
additional_keys = {"order_id": f"{uuid4()}"}
22+
custom_key = "order_id"
23+
additional_keys = {custom_key: f"{uuid4()}"}
3124
payload = json.dumps({"message": message, "append_keys": additional_keys})
3225

3326
# WHEN
3427
_, execution_time = data_fetcher.get_lambda_response(lambda_arn=basic_handler_fn_arn, payload=payload)
35-
filtered_logs = data_fetcher.get_logs(function_name=basic_handler_fn, start_time=execution_time)
28+
logs = data_fetcher.get_logs(function_name=basic_handler_fn, start_time=execution_time)
3629

3730
# THEN
38-
assert all(keys in logs.dict(exclude_unset=True) for logs in filtered_logs for keys in required_keys)
39-
assert any(getattr(logs, "order_id", False) for logs in filtered_logs)
31+
assert len(logs) == 1
32+
assert len(logs.get_cold_start_log()) == 1
33+
assert len(logs.get_log(key=custom_key)) == 1
34+
assert logs.have_logger_context_keys() is True

tests/e2e/utils/data_fetcher/logs.py

+61-22
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from pydantic import BaseModel, Extra
88
from retry import retry
99

10+
from aws_lambda_powertools.shared.constants import LOGGER_LAMBDA_CONTEXT_KEYS
11+
1012

1113
class Log(BaseModel, extra=Extra.allow):
1214
level: str
@@ -22,31 +24,68 @@ class Log(BaseModel, extra=Extra.allow):
2224
xray_trace_id: Optional[str]
2325

2426

27+
class LogFetcher:
28+
def __init__(
29+
self,
30+
function_name: str,
31+
start_time: datetime,
32+
log_client: Optional[CloudWatchLogsClient] = None,
33+
filter_expression: Optional[str] = None,
34+
) -> None:
35+
self.function_name = function_name
36+
self.start_time = int(start_time.timestamp())
37+
self.log_client = log_client or boto3.client("logs")
38+
self.filter_expression = filter_expression or "message" # Logger message key
39+
self.log_group = f"/aws/lambda/{self.function_name}"
40+
self.logs: List[Log] = self.get_logs()
41+
42+
def get_logs(self):
43+
ret = self.log_client.filter_log_events(
44+
logGroupName=self.log_group,
45+
startTime=self.start_time,
46+
filterPattern=self.filter_expression,
47+
)
48+
49+
if not ret["events"]:
50+
raise ValueError("Empty response from Cloudwatch Logs. Repeating...")
51+
52+
filtered_logs = []
53+
for event in ret["events"]:
54+
try:
55+
message = Log(**json.loads(event["message"]))
56+
except json.decoder.JSONDecodeError:
57+
continue
58+
filtered_logs.append(message)
59+
60+
return filtered_logs
61+
62+
def get_log(self, key: str, value: Optional[any] = None) -> List[Log]:
63+
logs = []
64+
for log in self.logs:
65+
log_value = getattr(log, key, None)
66+
if value is not None and log_value == value:
67+
logs.append(log)
68+
if value is None and getattr(log, key, False):
69+
logs.append(log)
70+
return logs
71+
72+
def get_cold_start_log(self) -> List[Log]:
73+
return [log for log in self.logs if log.cold_start]
74+
75+
def have_logger_context_keys(self) -> bool:
76+
return all(getattr(log, key, False) for log in self.logs for key in LOGGER_LAMBDA_CONTEXT_KEYS)
77+
78+
def __len__(self) -> int:
79+
return len(self.logs)
80+
81+
2582
@retry(ValueError, delay=2, jitter=1.5, tries=10)
2683
def get_logs(
2784
function_name: str,
2885
start_time: datetime,
29-
log_client: Optional[CloudWatchLogsClient] = None,
3086
filter_expression: Optional[str] = None,
31-
) -> List[Log]:
32-
log_client = log_client or boto3.client("logs")
33-
filter_expression = filter_expression or "message" # Logger message key
34-
35-
response = log_client.filter_log_events(
36-
logGroupName=f"/aws/lambda/{function_name}",
37-
startTime=int(start_time.timestamp()),
38-
filterPattern=filter_expression,
87+
log_client: Optional[CloudWatchLogsClient] = None,
88+
) -> LogFetcher:
89+
return LogFetcher(
90+
function_name=function_name, start_time=start_time, filter_expression=filter_expression, log_client=log_client
3991
)
40-
41-
if not response["events"]:
42-
raise ValueError("Empty response from Cloudwatch Logs. Repeating...")
43-
44-
filtered_logs = []
45-
for event in response["events"]:
46-
try:
47-
message = Log(**json.loads(event["message"]))
48-
except json.decoder.JSONDecodeError:
49-
continue
50-
filtered_logs.append(message)
51-
52-
return filtered_logs

0 commit comments

Comments
 (0)