diff --git a/.gitignore b/.gitignore index d053f6b9248..68c23bc3257 100644 --- a/.gitignore +++ b/.gitignore @@ -291,5 +291,5 @@ $RECYCLE.BIN/ # End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode # Misc - -test_report \ No newline at end of file +test_report +/.idea/* diff --git a/python/aws_lambda_powertools/logging/logger.py b/python/aws_lambda_powertools/logging/logger.py index 4b9a232bad9..84e5b1b4e0a 100644 --- a/python/aws_lambda_powertools/logging/logger.py +++ b/python/aws_lambda_powertools/logging/logger.py @@ -2,11 +2,12 @@ import itertools import logging import os +import random from distutils.util import strtobool from typing import Any, Callable, Dict -from . import aws_lambda_logging from ..helper.models import MetricUnit, build_lambda_context_model, build_metric_unit_from_str +from . import aws_lambda_logging logger = logging.getLogger(__name__) logger.setLevel(os.getenv("LOG_LEVEL", "INFO")) @@ -14,7 +15,9 @@ is_cold_start = True -def logger_setup(service: str = "service_undefined", level: str = "INFO", **kwargs): +def logger_setup( + service: str = "service_undefined", level: str = "INFO", sampling_rate: float = 0.0, **kwargs +): """Setups root logger to format statements in JSON. Includes service name and any additional key=value into logs @@ -26,6 +29,8 @@ def logger_setup(service: str = "service_undefined", level: str = "INFO", **kwar service name LOG_LEVEL: str logging level (e.g. INFO, DEBUG) + POWERTOOLS_LOGGER_SAMPLE_RATE: float + samping rate ranging from 0 to 1, 1 being 100% sampling Parameters ---------- @@ -33,6 +38,8 @@ def logger_setup(service: str = "service_undefined", level: str = "INFO", **kwar service name to be appended in logs, by default "service_undefined" level : str, optional logging.level, by default "INFO" + sample_rate: float, optional + sample rate for debug calls within execution context defaults to 0 Example ------- @@ -47,6 +54,7 @@ def logger_setup(service: str = "service_undefined", level: str = "INFO", **kwar Setups structured logging in JSON for Lambda functions using env vars $ export POWERTOOLS_SERVICE_NAME="payment" + $ export POWERTOOLS_LOGGER_SAMPLE_RATE=0.01 # 1% debug sampling >>> from aws_lambda_powertools.logging import logger_setup >>> logger = logger_setup() >>> @@ -55,12 +63,24 @@ def logger_setup(service: str = "service_undefined", level: str = "INFO", **kwar """ service = os.getenv("POWERTOOLS_SERVICE_NAME") or service + sampling_rate = os.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE") or sampling_rate log_level = os.getenv("LOG_LEVEL") or level logger = logging.getLogger(name=service) + + try: + if sampling_rate and random.random() <= float(sampling_rate): + log_level = logging.DEBUG + except ValueError: + raise ValueError( + f"Expected a float value ranging 0 to 1, but received {sampling_rate} instead. Please review POWERTOOLS_LOGGER_SAMPLE_RATE environment variable." + ) + logger.setLevel(log_level) # Patch logger by structuring its outputs as JSON - aws_lambda_logging.setup(level=log_level, service=service, **kwargs) + aws_lambda_logging.setup( + level=log_level, service=service, sampling_rate=sampling_rate, **kwargs + ) return logger diff --git a/python/tests/functional/test_aws_lambda_logging.py b/python/tests/functional/test_aws_lambda_logging.py index 79251a25274..6d5efd492a7 100644 --- a/python/tests/functional/test_aws_lambda_logging.py +++ b/python/tests/functional/test_aws_lambda_logging.py @@ -112,4 +112,4 @@ class X: log_dict = json.loads(stdout.getvalue()) - assert log_dict["message"]["x"].startswith("<") \ No newline at end of file + assert log_dict["message"]["x"].startswith("<") diff --git a/python/tests/functional/test_logger.py b/python/tests/functional/test_logger.py index f957c93c8f4..70d5f0c7899 100644 --- a/python/tests/functional/test_logger.py +++ b/python/tests/functional/test_logger.py @@ -80,6 +80,24 @@ def test_setup_service_env_var(monkeypatch, root_logger, stdout): assert service_name == log["service"] +def test_setup_sampling_rate(monkeypatch, root_logger, stdout): + # GIVEN samping rate is explicitly defined via POWERTOOLS_LOGGER_SAMPLE_RATE env + # WHEN logger is setup + # THEN sampling rate should be equals POWERTOOLS_LOGGER_SAMPLE_RATE value and should sample debug logs + + sampling_rate = "1" + monkeypatch.setenv("POWERTOOLS_LOGGER_SAMPLE_RATE", sampling_rate) + monkeypatch.setenv("LOG_LEVEL", "INFO") + + logger = logger_setup() + logger.debug("I am being sampled") + log = json.loads(stdout.getvalue()) + + assert sampling_rate == log["sampling_rate"] + assert "DEBUG" == log["level"] + assert "I am being sampled" == log["message"] + + def test_inject_lambda_context(root_logger, stdout, lambda_context): # GIVEN a lambda function is decorated with logger # WHEN logger is setup @@ -130,7 +148,6 @@ def handler(event, context): def test_inject_lambda_context_log_event_request_env_var( monkeypatch, root_logger, stdout, lambda_context ): - # GIVEN a lambda function is decorated with logger instructed to log event # via POWERTOOLS_LOGGER_LOG_EVENT env # WHEN logger is setup