Skip to content

Commit 3e0116b

Browse files
committed
feat(logging): suppress some log keys
Some keys like `location` and `timestamp` can be suppressed by setting them to None. Changes: * logger.py - Convert some methods to static functions * test_aws_lambda_logging - Add more tests and some which can be used for documentation
1 parent e148ffc commit 3e0116b

File tree

3 files changed

+62
-29
lines changed

3 files changed

+62
-29
lines changed

aws_lambda_powertools/logging/formatter.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, **kwargs):
2727

2828
super(JsonFormatter, self).__init__(datefmt=datefmt)
2929
self.reserved_keys = ["timestamp", "level", "location"]
30-
self.format_dict = dict.fromkeys(kwargs.pop("format_key", ["level", "location", "message", "timestamp"]))
30+
self.format_dict = dict.fromkeys(kwargs.pop("format_keys", ["level", "location", "message", "timestamp"]))
3131
self.format_dict.update(
3232
{"level": "%(levelname)s", "location": "%(funcName)s:%(lineno)d", "timestamp": "%(asctime)s"}
3333
)
@@ -71,4 +71,7 @@ def format(self, record): # noqa: A003
7171
if record.exc_text:
7272
log_dict["exception"] = record.exc_text
7373

74+
# Filter out top level key with values that are None
75+
log_dict = {k: v for k, v in log_dict.items() if v is not None}
76+
7477
return json.dumps(log_dict, default=self.default_json_formatter)

aws_lambda_powertools/logging/logger.py

+27-23
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def __init__(
123123
):
124124
self.service = service or os.getenv("POWERTOOLS_SERVICE_NAME") or "service_undefined"
125125
self.sampling_rate = sampling_rate or os.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE") or 0.0
126-
self.log_level = self._get_log_level(level)
126+
self.log_level = _get_log_level(level)
127127
self.child = child
128128
self._handler = logging.StreamHandler(stream) if stream is not None else logging.StreamHandler(sys.stdout)
129129
self._default_log_keys = {"service": self.service, "sampling_rate": self.sampling_rate}
@@ -136,35 +136,14 @@ def __getattr__(self, name):
136136
# https://github.com/awslabs/aws-lambda-powertools-python/issues/97
137137
return getattr(self._logger, name)
138138

139-
def _get_log_level(self, level: Union[str, int]) -> Union[str, int]:
140-
""" Returns preferred log level set by the customer in upper case """
141-
if isinstance(level, int):
142-
return level
143-
144-
log_level: str = level or os.getenv("LOG_LEVEL")
145-
log_level = log_level.upper() if log_level is not None else logging.INFO
146-
147-
return log_level
148-
149139
def _get_logger(self):
150140
""" Returns a Logger named {self.service}, or {self.service.filename} for child loggers"""
151141
logger_name = self.service
152142
if self.child:
153-
logger_name = f"{self.service}.{self._get_caller_filename()}"
143+
logger_name = f"{self.service}.{_get_caller_filename()}"
154144

155145
return logging.getLogger(logger_name)
156146

157-
def _get_caller_filename(self):
158-
""" Return caller filename by finding the caller frame """
159-
# Current frame => _get_logger()
160-
# Previous frame => logger.py
161-
# Before previous frame => Caller
162-
frame = inspect.currentframe()
163-
caller_frame = frame.f_back.f_back.f_back
164-
filename = caller_frame.f_globals["__name__"]
165-
166-
return filename
167-
168147
def _init_logger(self, **kwargs):
169148
"""Configures new logger"""
170149

@@ -207,6 +186,8 @@ def inject_lambda_context(self, lambda_handler: Callable[[Dict, Any], Any] = Non
207186
208187
Parameters
209188
----------
189+
lambda_handler : Callable
190+
Method to inject the lambda context
210191
log_event : bool, optional
211192
Instructs logger to log Lambda Event, by default False
212193
@@ -292,6 +273,29 @@ def structure_logs(self, append: bool = False, **kwargs):
292273
handler.setFormatter(JsonFormatter(**self._default_log_keys, **kwargs))
293274

294275

276+
def _get_log_level(level: Union[str, int]) -> Union[str, int]:
277+
""" Returns preferred log level set by the customer in upper case """
278+
if isinstance(level, int):
279+
return level
280+
281+
log_level: str = level or os.getenv("LOG_LEVEL")
282+
log_level = log_level.upper() if log_level is not None else logging.INFO
283+
284+
return log_level
285+
286+
287+
def _get_caller_filename():
288+
""" Return caller filename by finding the caller frame """
289+
# Current frame => _get_logger()
290+
# Previous frame => logger.py
291+
# Before previous frame => Caller
292+
frame = inspect.currentframe()
293+
caller_frame = frame.f_back.f_back.f_back
294+
filename = caller_frame.f_globals["__name__"]
295+
296+
return filename
297+
298+
295299
def set_package_logger(
296300
level: Union[str, int] = logging.DEBUG, stream: sys.stdout = None, formatter: logging.Formatter = None
297301
):

tests/functional/test_aws_lambda_logging.py

+31-5
Original file line numberDiff line numberDiff line change
@@ -127,18 +127,44 @@ def test_log_dict_key_seq(stdout):
127127

128128
log_dict: dict = json.loads(stdout.getvalue())
129129

130-
# THEN the key sequence should be `level,location,message,timestamp`
130+
# THEN the beginning key sequence must be `level,location,message,timestamp`
131131
assert ",".join(list(log_dict.keys())[:4]) == "level,location,message,timestamp"
132132

133133

134134
def test_log_dict_key_custom_seq(stdout):
135-
# GIVEN any logger configuration
136-
logger = Logger(level="INFO", stream=stdout, another="xxx", format_key=["message"])
135+
# GIVEN a logger configuration with format_keys set to ["message"]
136+
logger = Logger(stream=stdout, format_keys=["message"])
137137

138138
# WHEN logging a message
139139
logger.info("Message")
140140

141141
log_dict: dict = json.loads(stdout.getvalue())
142142

143-
# THEN the key sequence should be `level,location,message,timestamp`
144-
assert ",".join(list(log_dict.keys())[:4]) == "message,level,location,timestamp"
143+
# THEN the first key should be "message"
144+
assert list(log_dict.keys())[0] == "message"
145+
146+
147+
def test_log_custom_formatting(stdout):
148+
# GIVEN a logger where we have a custom location format
149+
logger = Logger(stream=stdout, location="[%(funcName)s] %(module)s")
150+
151+
# WHEN logging a message
152+
logger.info("foo")
153+
154+
log_dict: dict = json.loads(stdout.getvalue())
155+
156+
# THEN the `location` match the formatting
157+
assert log_dict["location"] == "[test_log_custom_formatting] test_aws_lambda_logging"
158+
159+
160+
def test_log_dict_key_strip_nones(stdout):
161+
# GIVEN a logger confirmation where we set `location` and `timestamp` to None
162+
logger = Logger(stream=stdout, location=None, timestamp=None)
163+
164+
# WHEN logging a message
165+
logger.info("foo")
166+
167+
log_dict: dict = json.loads(stdout.getvalue())
168+
169+
# THEN the keys should only include `level`, `message`, `service`, `sampling_rate`
170+
assert sorted(log_dict.keys()) == ["level", "message", "sampling_rate", "service"]

0 commit comments

Comments
 (0)