Skip to content

refactor(logging): add from __future__ import annotations #4940

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 4 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 19 additions & 17 deletions aws_lambda_powertools/logging/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
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 TYPE_CHECKING, Any, Callable, Iterable

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

if TYPE_CHECKING:
from aws_lambda_powertools.logging.types import LogRecord, LogStackTrace

RESERVED_LOG_ATTRS = (
"name",
"msg",
Expand Down Expand Up @@ -48,7 +50,7 @@ class BasePowertoolsFormatter(logging.Formatter, metaclass=ABCMeta):
def append_keys(self, **additional_keys) -> None:
raise NotImplementedError()

def get_current_keys(self) -> Dict[str, Any]:
def get_current_keys(self) -> dict[str, Any]:
return {}

def remove_keys(self, keys: Iterable[str]) -> None:
Expand All @@ -74,11 +76,11 @@ class LambdaPowertoolsFormatter(BasePowertoolsFormatter):
def __init__(
self,
json_serializer: Callable[[LogRecord], str] | None = None,
json_deserializer: Callable[[Dict | str | bool | int | float], 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: List[str] | None = None,
log_record_order: list[str] | None = None,
utc: bool = False,
use_rfc3339: bool = False,
serialize_stacktrace: bool = True,
Expand Down Expand Up @@ -182,7 +184,7 @@ def format(self, record: logging.LogRecord) -> str: # noqa: A003

return self.serialize(log=formatted_log)

def formatTime(self, record: logging.LogRecord, datefmt: Optional[str] = None) -> str:
def formatTime(self, record: logging.LogRecord, datefmt: str | None = None) -> str:
# As of Py3.7, we can infer milliseconds directly from any datetime
# saving processing time as we can shortcircuit early
# Maintenance: In V3, we (and Java) should move to this format by default
Expand Down Expand Up @@ -234,7 +236,7 @@ def formatTime(self, record: logging.LogRecord, datefmt: Optional[str] = None) -
def append_keys(self, **additional_keys) -> None:
self.log_format.update(additional_keys)

def get_current_keys(self) -> Dict[str, Any]:
def get_current_keys(self) -> dict[str, Any]:
return self.log_format

def remove_keys(self, keys: Iterable[str]) -> None:
Expand All @@ -246,14 +248,14 @@ def clear_state(self) -> None:
self.log_format.update(**self.keys_combined)

@staticmethod
def _build_default_keys() -> Dict[str, str]:
def _build_default_keys() -> dict[str, str]:
return {
"level": "%(levelname)s",
"location": "%(funcName)s:%(lineno)d",
"timestamp": "%(asctime)s",
}

def _get_latest_trace_id(self) -> Optional[str]:
def _get_latest_trace_id(self) -> str | None:
xray_trace_id_key = self.log_format.get("xray_trace_id", "")
if xray_trace_id_key is None:
# key is explicitly disabled; ignore it. e.g., Logger(xray_trace_id=None)
Expand All @@ -262,7 +264,7 @@ def _get_latest_trace_id(self) -> Optional[str]:
xray_trace_id = os.getenv(constants.XRAY_TRACE_ID_ENV)
return xray_trace_id.split(";")[0].replace("Root=", "") if xray_trace_id else None

def _extract_log_message(self, log_record: logging.LogRecord) -> Union[Dict[str, Any], str, bool, Iterable]:
def _extract_log_message(self, log_record: logging.LogRecord) -> dict[str, Any] | str | bool | Iterable:
"""Extract message from log record and attempt to JSON decode it if str

Parameters
Expand All @@ -272,7 +274,7 @@ def _extract_log_message(self, log_record: logging.LogRecord) -> Union[Dict[str,

Returns
-------
message: Union[Dict, str, bool, Iterable]
message: dict[str, Any] | str | bool | Iterable
Extracted message
"""
message = log_record.msg
Expand Down Expand Up @@ -308,7 +310,7 @@ def _serialize_stacktrace(self, log_record: logging.LogRecord) -> LogStackTrace

return None

def _extract_log_exception(self, log_record: logging.LogRecord) -> Union[Tuple[str, str], Tuple[None, None]]:
def _extract_log_exception(self, log_record: logging.LogRecord) -> tuple[str, str] | tuple[None, None]:
"""Format traceback information, if available

Parameters
Expand All @@ -318,15 +320,15 @@ def _extract_log_exception(self, log_record: logging.LogRecord) -> Union[Tuple[s

Returns
-------
log_record: Optional[Tuple[str, str]]
log_record: tuple[str, str] | tuple[None, None]
Log record with constant traceback info and exception name
"""
if log_record.exc_info:
return self.formatException(log_record.exc_info), log_record.exc_info[0].__name__ # type: ignore

return None, None

def _extract_log_keys(self, log_record: logging.LogRecord) -> Dict[str, Any]:
def _extract_log_keys(self, log_record: logging.LogRecord) -> dict[str, Any]:
"""Extract and parse custom and reserved log keys

Parameters
Expand All @@ -336,7 +338,7 @@ def _extract_log_keys(self, log_record: logging.LogRecord) -> Dict[str, Any]:

Returns
-------
formatted_log: Dict
formatted_log: dict[str, Any]
Structured log as dictionary
"""
record_dict = log_record.__dict__.copy()
Expand All @@ -358,7 +360,7 @@ def _extract_log_keys(self, log_record: logging.LogRecord) -> Dict[str, Any]:
return formatted_log

@staticmethod
def _strip_none_records(records: Dict[str, Any]) -> Dict[str, Any]:
def _strip_none_records(records: dict[str, Any]) -> dict[str, Any]:
"""Remove any key with None as value"""
return {k: v for k, v in records.items() if v is not None}

Expand All @@ -367,4 +369,4 @@ def _strip_none_records(records: Dict[str, Any]) -> Dict[str, Any]:


# Fetch current and future parameters from PowertoolsFormatter that should be reserved
RESERVED_FORMATTER_CUSTOM_KEYS: List[str] = inspect.getfullargspec(LambdaPowertoolsFormatter).args[1:]
RESERVED_FORMATTER_CUSTOM_KEYS: list[str] = inspect.getfullargspec(LambdaPowertoolsFormatter).args[1:]
8 changes: 5 additions & 3 deletions aws_lambda_powertools/logging/formatters/datadog.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
from __future__ import annotations

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

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

if TYPE_CHECKING:
from aws_lambda_powertools.logging.types import LogRecord


class DatadogLogFormatter(LambdaPowertoolsFormatter):
def __init__(
self,
json_serializer: Callable[[LogRecord], str] | None = None,
json_deserializer: Callable[[Dict | str | bool | int | float], 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
Loading
Loading