Skip to content
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

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

Merged
merged 19 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0
rev: "f71fa2c1f9cf5cb705f73dffe4b21f7c61470ba9" # v4.4.0
hooks:
- id: check-merge-conflict
- id: trailing-whitespace
Expand All @@ -30,9 +30,9 @@ repos:
language: system
types: [python]
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: "11c08644ce6df850480d98f628596446a526cbc6" # frozen: v0.31.1
rev: "ce0d77ac47dc921b62429804fe763d4d35f66a76" # v0.34.0
hooks:
- id: markdownlint
- id: markdownlint-docker
args: ["--fix"]
- repo: local
hooks:
Expand All @@ -43,7 +43,7 @@ repos:
types: [yaml]
files: examples/.*
- repo: https://github.com/rhysd/actionlint
rev: v1.6.23
rev: "fd7ba3c382e13dcc0248e425b4cbc3f1185fa3ee" # v1.6.24
hooks:
- id: actionlint-docker
args: [-pyflakes=]
19 changes: 11 additions & 8 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 @@ -8,8 +10,9 @@
from functools import partial
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union

from ..shared import constants
from ..shared.functions import powertools_dev_is_set
from aws_lambda_powertools.logging.types import LogRecord
from aws_lambda_powertools.shared import constants
from aws_lambda_powertools.shared.functions import powertools_dev_is_set

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

def __init__(
self,
json_serializer: Optional[Callable[[Dict], 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,
json_serializer: Callable[[LogRecord], 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,
use_datetime_directive: bool = False,
log_record_order: Optional[List[str]] = None,
log_record_order: List[str] | None = None,
utc: bool = False,
use_rfc3339: bool = False,
**kwargs,
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: LogRecord) -> str:
"""Serialize structured log dict to JSON str"""
return self.json_serializer(log)

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

from typing import Any, Callable
from typing import Any, Callable, Dict

from aws_lambda_powertools.logging.formatter import LambdaPowertoolsFormatter
from aws_lambda_powertools.logging.types import LogRecord


class DatadogLogFormatter(LambdaPowertoolsFormatter):
def __init__(
self,
json_serializer: Callable[[dict], str] | None = None,
json_deserializer: Callable[[dict | str | bool | int | float], str] | None = None,
json_serializer: Callable[[LogRecord], 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,
use_datetime_directive: bool = False,
Expand Down
50 changes: 50 additions & 0 deletions aws_lambda_powertools/logging/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from __future__ import annotations

erikayao93 marked this conversation as resolved.
Show resolved Hide resolved
import sys

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

if sys.version_info >= (3, 8):
from typing import TypedDict
else:
from typing_extensions import TypedDict

if sys.version_info >= (3, 10):
from typing import TypeAlias
else:
from typing_extensions import TypeAlias

from typing import Any, Dict, List, Union

LogRecord: TypeAlias = Union[Dict[str, Any], "PowertoolsLogRecord"]


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]
erikayao93 marked this conversation as resolved.
Show resolved Hide resolved

# From logger.set_correlation_id
correlation_id: NotRequired[str]

# Fields from logger.exception
exception_name: NotRequired[str]
exception: NotRequired[str]
2 changes: 1 addition & 1 deletion docs/core/logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ For these, you can override the `serialize` method from [LambdaPowertoolsFormatt

=== "bring_your_own_formatter.py"

```python hl_lines="2 5-6 12"
```python hl_lines="2-3 6 11-12 15"
--8<-- "examples/logger/src/bring_your_own_formatter.py"
```

Expand Down
9 changes: 6 additions & 3 deletions examples/logger/src/bring_your_own_formatter.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from aws_lambda_powertools import Logger
from aws_lambda_powertools.logging.formatter import LambdaPowertoolsFormatter
from aws_lambda_powertools.logging.types import LogRecord


class CustomFormatter(LambdaPowertoolsFormatter):
def serialize(self, log: dict) -> str:
def serialize(self, log: LogRecord) -> 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
# in this example, log["message"] is a required field
# but we want to remap to "event" and delete "message", hence mypy ignore checks
log["event"] = log.pop("message") # type: ignore[typeddict-unknown-key,misc]
return self.json_serializer(log)


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