Skip to content

feat(logger): type log record in LambdaPowertoolsFormatter with TypedDict #2419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c09e297
Added types.py for new TypedLog class
erikayao93 Jun 7, 2023
652ee8a
Added additional typing options for TypedLog keys, updated bring_your…
erikayao93 Jun 7, 2023
9289c4b
Added missing space to satisfy linter
erikayao93 Jun 7, 2023
320a728
Merge branch 'develop' into logger-typing
heitorlessa Jun 8, 2023
c0ea2fc
Merge branch 'develop' into logger-typing
leandrodamascena Jun 8, 2023
d49f22b
Edited types.py for improved TypedDict syntax, reverted changes to ex…
erikayao93 Jun 8, 2023
7da4a2c
Attempted changes to formatters to accomodate TypedDict typing
erikayao93 Jun 8, 2023
ffb8243
Update pre-commit for cloud desktop environment
erikayao93 Jun 9, 2023
85ff2a1
Update pre-commit attempt
erikayao93 Jun 9, 2023
39656fc
Updated import logic for TypedDict
erikayao93 Jun 9, 2023
80fcae3
chore: break import into two branches to ease reading
heitorlessa Jun 14, 2023
1a3d33e
chore: introduce LogRecord type alias for PowertoolsLogRecord and Dict
heitorlessa Jun 14, 2023
77c08c4
fix: ignore typeddict specific checks and explain why
heitorlessa Jun 14, 2023
e95b4a3
Merge branch 'develop' into logger-typing
heitorlessa Jun 14, 2023
2b504ed
chore: move to pipe union annotation; fqdn imports
heitorlessa Jun 14, 2023
b1e6a17
chore: fix docs highlighting
heitorlessa Jun 14, 2023
26dc9bf
chore: use immutable version for markdownlint-cli
heitorlessa Jun 14, 2023
318255e
chore: use immutable version for actionlint
heitorlessa Jun 14, 2023
70e9c2a
chore: use immutable version for pre-commit-hooks
heitorlessa Jun 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions aws_lambda_powertools/logging/formatter.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import inspect
import json
import logging
Expand All @@ -6,10 +8,11 @@
from abc import ABCMeta, abstractmethod
from datetime import datetime, timezone
from functools import partial
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Union

from ..shared import constants
from ..shared.functions import powertools_dev_is_set
from .types import PowertoolsLogRecord

RESERVED_LOG_ATTRS = (
"name",
Expand Down Expand Up @@ -66,7 +69,7 @@ class LambdaPowertoolsFormatter(BasePowertoolsFormatter):

def __init__(
self,
json_serializer: Optional[Callable[[Dict], str]] = None,
json_serializer: Optional[Callable[[Mapping], str]] = None,
json_deserializer: Optional[Callable[[Union[Dict, str, bool, int, float]], str]] = None,
json_default: Optional[Callable[[Any], Any]] = None,
datefmt: Optional[str] = None,
Expand Down Expand Up @@ -144,7 +147,7 @@ def __init__(

super().__init__(datefmt=self.datefmt)

def serialize(self, log: Dict) -> str:
def serialize(self, log: PowertoolsLogRecord | Mapping) -> str:
"""Serialize structured log dict to JSON str"""
return self.json_serializer(log)

Expand Down
4 changes: 2 additions & 2 deletions aws_lambda_powertools/logging/formatters/datadog.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from __future__ import annotations

from typing import Any, Callable
from typing import Any, Callable, Mapping

from aws_lambda_powertools.logging.formatter import LambdaPowertoolsFormatter


class DatadogLogFormatter(LambdaPowertoolsFormatter):
def __init__(
self,
json_serializer: Callable[[dict], str] | None = None,
json_serializer: Callable[[Mapping], str] | None = None,
json_deserializer: Callable[[dict | str | bool | int | float], str] | None = None,
json_default: Callable[[Any], Any] | None = None,
datefmt: str | None = None,
Expand Down
38 changes: 38 additions & 0 deletions aws_lambda_powertools/logging/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from __future__ import annotations

import sys

if sys.version_info < (3, 11):
from typing_extensions import NotRequired, TypedDict
else:
from typing import NotRequired, TypedDict

from typing import Any, Dict, List


class PowertoolsLogRecord(TypedDict):
# Base fields (required)
level: str
location: str
message: Dict[str, Any] | str | bool | List[Any]
timestamp: str | int
service: str

# Fields from logger.inject_lambda_context
cold_start: NotRequired[bool]
function_name: NotRequired[str]
function_memory_size: NotRequired[int]
function_arn: NotRequired[str]
function_request_id: NotRequired[str]
# From logger.inject_lambda_context if AWS X-Ray is enabled
xray_trace_id: NotRequired[str]

# If sample_rate is defined
sampling_rate: NotRequired[float]

# From logger.set_correlation_id
correlation_id: NotRequired[str]

# Fields from logger.exception
exception_name: NotRequired[str]
exception: NotRequired[str]
11 changes: 8 additions & 3 deletions examples/logger/src/bring_your_own_formatter.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from __future__ import annotations

from typing import Mapping

from aws_lambda_powertools import Logger
from aws_lambda_powertools.logging.formatter import LambdaPowertoolsFormatter
from aws_lambda_powertools.logging.types import PowertoolsLogRecord


class CustomFormatter(LambdaPowertoolsFormatter):
def serialize(self, log: dict) -> str:
def serialize(self, log: PowertoolsLogRecord | Mapping) -> str:
"""Serialize final structured log dict to JSON str"""
log["event"] = log.pop("message") # rename message key to event
return self.json_serializer(log) # use configured json serializer
log["event"] = log.pop("message") # type: ignore
return self.json_serializer(log)


logger = Logger(service="payment", logger_formatter=CustomFormatter())
Expand Down