Skip to content

Commit 3a0ce69

Browse files
committed
fix lambda local tz
1 parent 517622b commit 3a0ce69

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

aws_lambda_powertools/logging/formatter.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import json
55
import logging
66
import os
7-
import time
87
from abc import ABCMeta, abstractmethod
98
from datetime import datetime, timezone
109
from functools import partial
@@ -42,6 +41,12 @@
4241
)
4342

4443

44+
def local_tz_converter(secs: float, utc: bool) -> datetime:
45+
if utc:
46+
return datetime.fromtimestamp(secs, tz=timezone.utc)
47+
return datetime.fromtimestamp(secs).astimezone()
48+
49+
4550
class BasePowertoolsFormatter(logging.Formatter, metaclass=ABCMeta):
4651
@abstractmethod
4752
def append_keys(self, **additional_keys) -> None:
@@ -142,9 +147,6 @@ def __init__(
142147
self.update_formatter = self.append_keys # alias to old method
143148
self.use_rfc3339_iso8601 = use_rfc3339
144149

145-
if self.utc:
146-
self.converter = time.gmtime
147-
148150
self.keys_combined = {**self._build_default_keys(), **kwargs}
149151
self.log_format.update(**self.keys_combined)
150152

@@ -178,11 +180,10 @@ def formatTime(self, record: logging.LogRecord, datefmt: Optional[str] = None) -
178180
ts_as_datetime = datetime.fromtimestamp(record.created, tz=timezone.utc)
179181
else:
180182
ts_as_datetime = datetime.fromtimestamp(record.created).astimezone()
181-
182183
return ts_as_datetime.isoformat(timespec="milliseconds") # 2022-10-27T17:42:26.841+0200
183184

184185
# converts to local/UTC TZ as struct time
185-
record_ts = self.converter(record.created)
186+
record_ts = local_tz_converter(record.created, self.utc)
186187

187188
if datefmt is None: # pragma: no cover, it'll always be None in std logging, but mypy
188189
datefmt = self.datefmt
@@ -211,11 +212,11 @@ def formatTime(self, record: logging.LogRecord, datefmt: Optional[str] = None) -
211212
# Only time format codes being used
212213
elif datefmt:
213214
custom_fmt = datefmt.replace(self.custom_ms_time_directive, msecs)
214-
return time.strftime(custom_fmt, record_ts)
215+
return record_ts.strftime(custom_fmt)
215216

216217
# Use default fmt: 2021-05-03 10:20:19,650+0200
217218
custom_fmt = self.default_time_format.replace(self.custom_ms_time_directive, msecs)
218-
return time.strftime(custom_fmt, record_ts)
219+
return record_ts.strftime(custom_fmt)
219220

220221
def append_keys(self, **additional_keys) -> None:
221222
self.log_format.update(additional_keys)

tests/functional/test_logger.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import io
44
import json
55
import logging
6+
import os
67
import random
78
import re
89
import string
910
import sys
11+
import time
1012
import warnings
1113
from ast import Dict
1214
from collections import namedtuple
@@ -959,3 +961,36 @@ def test_stream_defaults_to_stdout(service_name, capsys):
959961
# NOTE: we can't assert on capsys.readouterr().err due to a known bug: https://github.com/pytest-dev/pytest/issues/5997
960962
log = json.loads(capsys.readouterr().out.strip())
961963
assert log["message"] == msg
964+
965+
966+
@pytest.mark.parametrize(
967+
"utc,tz",
968+
[(False, "Etc/GMT+11"), (True, "Etc/GMT+11"), (False, "US/Alaska"), (True, "US/Alaska")],
969+
)
970+
def test_tz_env_var(stdout, service_name, utc, tz, capsys):
971+
# GIVEN
972+
973+
# set tz env var to tz input
974+
os.environ["TZ"] = tz
975+
976+
# Refresh the local TZ to time lib, for we just changed tz env var
977+
# we don't need this in prod env. Time Class read this info at import
978+
time.tzset()
979+
980+
logger = Logger(
981+
service=service_name,
982+
datefmt="custom timestamp: milliseconds=%F microseconds=%f timezone=%z",
983+
utc=utc,
984+
)
985+
986+
# WHEN a log statement happens
987+
logger.info({})
988+
989+
# THEN the timestamp has the appropriate formatting
990+
log = json.loads(capsys.readouterr().out.strip())
991+
992+
expected_tz = datetime.now().astimezone(timezone.utc if utc else None).strftime("%z")
993+
assert re.fullmatch(
994+
f"custom timestamp: milliseconds=[0-9]+ microseconds=[0-9]+ timezone={re.escape(expected_tz)}",
995+
log["timestamp"],
996+
)

0 commit comments

Comments
 (0)