From 6a290917a8621fac965105ac8ab5abb62fe41d9f Mon Sep 17 00:00:00 2001 From: Peter Marko <40757147+pmarko1711@users.noreply.github.com> Date: Tue, 27 Sep 2022 22:45:53 +0000 Subject: [PATCH 1/6] feat(logger): Conditional JSON indentation for AWS SAM Local --- aws_lambda_powertools/logging/formatter.py | 8 +++++++- docs/core/logger.md | 3 +++ .../test_logger_powertools_formatter.py | 20 +++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/logging/formatter.py b/aws_lambda_powertools/logging/formatter.py index 1f01015051c..73db26359e5 100644 --- a/aws_lambda_powertools/logging/formatter.py +++ b/aws_lambda_powertools/logging/formatter.py @@ -111,9 +111,15 @@ def __init__( Key-value to be included in log messages """ + self.json_deserializer = json_deserializer or json.loads self.json_default = json_default or str - self.json_serializer = json_serializer or partial(json.dumps, default=self.json_default, separators=(",", ":")) + self.json_indent = ( + 4 if os.getenv("AWS_SAM_LOCAL", "").lower() == "true" else None + ) # indented json serialization when in AWS SAM Local + self.json_serializer = json_serializer or partial( + json.dumps, default=self.json_default, separators=(",", ":"), indent=self.json_indent + ) self.datefmt = datefmt self.use_datetime_directive = use_datetime_directive diff --git a/docs/core/logger.md b/docs/core/logger.md index c699568b349..dea071bbfd3 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -555,6 +555,9 @@ As parameters don't always translate well between them, you can pass any callabl --8<-- "examples/logger/src/bring_your_own_json_serializer.py" ``` +???+ info + When your code runs in [AWS SAM local invoke](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-invoke.html) (marked by the `AWS_SAM_LOCAL` env var), Logger's default `json.dumps` will apply indentation by four spaces. + ## Testing your code ### Inject Lambda Context diff --git a/tests/functional/test_logger_powertools_formatter.py b/tests/functional/test_logger_powertools_formatter.py index c9f970e29a5..580ef088556 100644 --- a/tests/functional/test_logger_powertools_formatter.py +++ b/tests/functional/test_logger_powertools_formatter.py @@ -1,6 +1,7 @@ """aws_lambda_logging tests.""" import io import json +import os import random import string import time @@ -288,3 +289,22 @@ def test_log_formatting(stdout, service_name): # THEN the formatting should be applied (NB. this is valid json, but hasn't be parsed) assert log_dict["message"] == '["foo bar 123 [1, None]", null]' + + +def test_log_json_indent_default(stdout, service_name, monkeypatch): + # GIVEN a logger with default settings while NOT in AWS SAM Local + if "AWS_SAM_LOCAL" in os.environ: + monkeypatch.delenv(name="AWS_SAM_LOCAL") + logger = Logger(service=service_name, stream=stdout) + logger.info("Test message") + # THEN the json should not be indented at all (using four blank spaces) + assert " " * 4 not in stdout.getvalue() + + +def test_log_json_indent_aws_sam_local(stdout, service_name, monkeypatch): + # GIVEN a logger with default settings while in AWS SAM Local + monkeypatch.setenv(name="AWS_SAM_LOCAL", value="true") + logger = Logger(service=service_name, stream=stdout) + logger.info("Test message") + # THEN the json should contain indentation (of four blank spaces) + assert " " * 4 in stdout.getvalue() From cdf92510b2f5f59aa0092e28e58dee5ae3f298f2 Mon Sep 17 00:00:00 2001 From: Peter Marko <40757147+pmarko1711@users.noreply.github.com> Date: Wed, 28 Sep 2022 20:38:39 +0000 Subject: [PATCH 2/6] feat(logger): Conditional JSON indentation on POWERTOOLS_DEV --- aws_lambda_powertools/logging/formatter.py | 2 +- aws_lambda_powertools/shared/constants.py | 3 +++ docs/core/logger.md | 6 ++--- .../test_logger_powertools_formatter.py | 23 ++++++++++--------- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/aws_lambda_powertools/logging/formatter.py b/aws_lambda_powertools/logging/formatter.py index 73db26359e5..ecfd38ddb3b 100644 --- a/aws_lambda_powertools/logging/formatter.py +++ b/aws_lambda_powertools/logging/formatter.py @@ -115,7 +115,7 @@ def __init__( self.json_deserializer = json_deserializer or json.loads self.json_default = json_default or str self.json_indent = ( - 4 if os.getenv("AWS_SAM_LOCAL", "").lower() == "true" else None + constants.PRETTY_INDENT if os.getenv("POWERTOOLS_DEV", "").lower() == "true" else constants.COMPACT_INDENT ) # indented json serialization when in AWS SAM Local self.json_serializer = json_serializer or partial( json.dumps, default=self.json_default, separators=(",", ":"), indent=self.json_indent diff --git a/aws_lambda_powertools/shared/constants.py b/aws_lambda_powertools/shared/constants.py index 48d94d88f1d..e850a794f41 100644 --- a/aws_lambda_powertools/shared/constants.py +++ b/aws_lambda_powertools/shared/constants.py @@ -32,3 +32,6 @@ "cold_start", "xray_trace_id", ] + +PRETTY_INDENT: int = 4 +COMPACT_INDENT = None diff --git a/docs/core/logger.md b/docs/core/logger.md index dea071bbfd3..9605017697c 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -369,6 +369,9 @@ If you prefer configuring it separately, or you'd want to bring this JSON Format | **`log_record_order`** | set order of log keys when logging | `["level", "location", "message", "timestamp"]` | | **`kwargs`** | key-value to be included in log messages | `None` | +???+ info + When `POWERTOOLS_DEV` env var is present and set to `"true"`, Logger's default serializer (`json.dumps`) will pretty-print log messages for easier readability. + ```python hl_lines="2 7-8" title="Pre-configuring Lambda Powertools Formatter" --8<-- "examples/logger/src/powertools_formatter_setup.py" ``` @@ -555,9 +558,6 @@ As parameters don't always translate well between them, you can pass any callabl --8<-- "examples/logger/src/bring_your_own_json_serializer.py" ``` -???+ info - When your code runs in [AWS SAM local invoke](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-invoke.html) (marked by the `AWS_SAM_LOCAL` env var), Logger's default `json.dumps` will apply indentation by four spaces. - ## Testing your code ### Inject Lambda Context diff --git a/tests/functional/test_logger_powertools_formatter.py b/tests/functional/test_logger_powertools_formatter.py index 580ef088556..b684eaca109 100644 --- a/tests/functional/test_logger_powertools_formatter.py +++ b/tests/functional/test_logger_powertools_formatter.py @@ -9,6 +9,7 @@ import pytest from aws_lambda_powertools import Logger +from aws_lambda_powertools.shared import constants @pytest.fixture @@ -291,20 +292,20 @@ def test_log_formatting(stdout, service_name): assert log_dict["message"] == '["foo bar 123 [1, None]", null]' -def test_log_json_indent_default(stdout, service_name, monkeypatch): - # GIVEN a logger with default settings while NOT in AWS SAM Local - if "AWS_SAM_LOCAL" in os.environ: - monkeypatch.delenv(name="AWS_SAM_LOCAL") +def test_log_json_indent_compact_indent(stdout, service_name, monkeypatch): + # GIVEN a logger with default settings and WHEN POWERTOOLS_DEV is not set + if "POWERTOOLS_DEV" in os.environ: + monkeypatch.delenv(name="POWERTOOLS_DEV") logger = Logger(service=service_name, stream=stdout) logger.info("Test message") - # THEN the json should not be indented at all (using four blank spaces) - assert " " * 4 not in stdout.getvalue() + # THEN the json should not be indented using constant.PRETTY_INDENT blank spaces + assert " " * constants.PRETTY_INDENT not in stdout.getvalue() -def test_log_json_indent_aws_sam_local(stdout, service_name, monkeypatch): - # GIVEN a logger with default settings while in AWS SAM Local - monkeypatch.setenv(name="AWS_SAM_LOCAL", value="true") +def test_log_json_pretty_indent(stdout, service_name, monkeypatch): + # GIVEN a logger with default settings and WHEN POWERTOOLS_DEV=="true" + monkeypatch.setenv(name="POWERTOOLS_DEV", value="true") logger = Logger(service=service_name, stream=stdout) logger.info("Test message") - # THEN the json should contain indentation (of four blank spaces) - assert " " * 4 in stdout.getvalue() + # THEN the json should contain indentation (of constant.PRETTY_INDENT blank spaces) + assert " " * constants.PRETTY_INDENT in stdout.getvalue() From 763f948c42909901584aecdaab0b428f89f91078 Mon Sep 17 00:00:00 2001 From: Peter Marko <40757147+pmarko1711@users.noreply.github.com> Date: Fri, 30 Sep 2022 19:45:12 +0200 Subject: [PATCH 3/6] Update aws_lambda_powertools/logging/formatter.py Co-authored-by: Heitor Lessa --- aws_lambda_powertools/logging/formatter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws_lambda_powertools/logging/formatter.py b/aws_lambda_powertools/logging/formatter.py index ecfd38ddb3b..95042059c90 100644 --- a/aws_lambda_powertools/logging/formatter.py +++ b/aws_lambda_powertools/logging/formatter.py @@ -115,7 +115,7 @@ def __init__( self.json_deserializer = json_deserializer or json.loads self.json_default = json_default or str self.json_indent = ( - constants.PRETTY_INDENT if os.getenv("POWERTOOLS_DEV", "").lower() == "true" else constants.COMPACT_INDENT + constants.PRETTY_INDENT if strtobool(os.getenv("POWERTOOLS_DEV", "0")) else constants.COMPACT_INDENT ) # indented json serialization when in AWS SAM Local self.json_serializer = json_serializer or partial( json.dumps, default=self.json_default, separators=(",", ":"), indent=self.json_indent From 31cfbe33d5dedba1d691256c8518b7a356bcebdd Mon Sep 17 00:00:00 2001 From: Peter Marko <40757147+pmarko1711@users.noreply.github.com> Date: Fri, 30 Sep 2022 19:45:21 +0200 Subject: [PATCH 4/6] Update aws_lambda_powertools/shared/constants.py Co-authored-by: Heitor Lessa --- aws_lambda_powertools/shared/constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aws_lambda_powertools/shared/constants.py b/aws_lambda_powertools/shared/constants.py index e850a794f41..09d9132af90 100644 --- a/aws_lambda_powertools/shared/constants.py +++ b/aws_lambda_powertools/shared/constants.py @@ -33,5 +33,6 @@ "xray_trace_id", ] +# JSON indentation level PRETTY_INDENT: int = 4 COMPACT_INDENT = None From c878a85a4e3284b7ec758a04575d0c776e670dcd Mon Sep 17 00:00:00 2001 From: Peter Marko <40757147+pmarko1711@users.noreply.github.com> Date: Fri, 30 Sep 2022 19:46:51 +0200 Subject: [PATCH 5/6] Update tests/functional/test_logger_powertools_formatter.py Co-authored-by: Heitor Lessa --- tests/functional/test_logger_powertools_formatter.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/functional/test_logger_powertools_formatter.py b/tests/functional/test_logger_powertools_formatter.py index b684eaca109..627ef0191ca 100644 --- a/tests/functional/test_logger_powertools_formatter.py +++ b/tests/functional/test_logger_powertools_formatter.py @@ -294,8 +294,7 @@ def test_log_formatting(stdout, service_name): def test_log_json_indent_compact_indent(stdout, service_name, monkeypatch): # GIVEN a logger with default settings and WHEN POWERTOOLS_DEV is not set - if "POWERTOOLS_DEV" in os.environ: - monkeypatch.delenv(name="POWERTOOLS_DEV") + monkeypatch.delenv(name="POWERTOOLS_DEV", raising=False) logger = Logger(service=service_name, stream=stdout) logger.info("Test message") # THEN the json should not be indented using constant.PRETTY_INDENT blank spaces From c67579ae760db295a24c7763175a1bdb6c2229f4 Mon Sep 17 00:00:00 2001 From: Peter Marko <40757147+pmarko1711@users.noreply.github.com> Date: Fri, 30 Sep 2022 20:08:18 +0000 Subject: [PATCH 6/6] Tests counting newlines only --- aws_lambda_powertools/logging/formatter.py | 1 + tests/functional/test_logger_powertools_formatter.py | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/aws_lambda_powertools/logging/formatter.py b/aws_lambda_powertools/logging/formatter.py index 95042059c90..51f60a87021 100644 --- a/aws_lambda_powertools/logging/formatter.py +++ b/aws_lambda_powertools/logging/formatter.py @@ -9,6 +9,7 @@ from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union from ..shared import constants +from ..shared.functions import strtobool RESERVED_LOG_ATTRS = ( "name", diff --git a/tests/functional/test_logger_powertools_formatter.py b/tests/functional/test_logger_powertools_formatter.py index 627ef0191ca..7276f49d487 100644 --- a/tests/functional/test_logger_powertools_formatter.py +++ b/tests/functional/test_logger_powertools_formatter.py @@ -9,7 +9,6 @@ import pytest from aws_lambda_powertools import Logger -from aws_lambda_powertools.shared import constants @pytest.fixture @@ -297,8 +296,9 @@ def test_log_json_indent_compact_indent(stdout, service_name, monkeypatch): monkeypatch.delenv(name="POWERTOOLS_DEV", raising=False) logger = Logger(service=service_name, stream=stdout) logger.info("Test message") - # THEN the json should not be indented using constant.PRETTY_INDENT blank spaces - assert " " * constants.PRETTY_INDENT not in stdout.getvalue() + # THEN the json should not have multiple lines + new_lines = stdout.getvalue().count(os.linesep) + assert new_lines == 1 def test_log_json_pretty_indent(stdout, service_name, monkeypatch): @@ -306,5 +306,6 @@ def test_log_json_pretty_indent(stdout, service_name, monkeypatch): monkeypatch.setenv(name="POWERTOOLS_DEV", value="true") logger = Logger(service=service_name, stream=stdout) logger.info("Test message") - # THEN the json should contain indentation (of constant.PRETTY_INDENT blank spaces) - assert " " * constants.PRETTY_INDENT in stdout.getvalue() + # THEN the json should contain more than line + new_lines = stdout.getvalue().count(os.linesep) + assert new_lines > 1