Skip to content
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

chore: move env names to constant file #264

Merged
merged 2 commits into from
Jan 17, 2021
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
4 changes: 3 additions & 1 deletion aws_lambda_powertools/logging/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import os
from typing import Dict, Iterable, Optional, Union

from ..shared import constants

STD_LOGGING_KEYS = (
"name",
"msg",
Expand Down Expand Up @@ -73,7 +75,7 @@ def _build_root_keys(**kwargs):

@staticmethod
def _get_latest_trace_id():
xray_trace_id = os.getenv("_X_AMZN_TRACE_ID")
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 update_formatter(self, **kwargs):
Expand Down
18 changes: 12 additions & 6 deletions aws_lambda_powertools/logging/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import os
import random
import sys
from distutils.util import strtobool
from typing import Any, Callable, Dict, Union

from ..shared import constants
from ..shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice
from .exceptions import InvalidLoggerSamplingRateError
from .filters import SuppressFilter
from .formatter import JsonFormatter
Expand Down Expand Up @@ -122,8 +123,12 @@ def __init__(
stream: sys.stdout = None,
**kwargs,
):
self.service = service or os.getenv("POWERTOOLS_SERVICE_NAME") or "service_undefined"
self.sampling_rate = sampling_rate or os.getenv("POWERTOOLS_LOGGER_SAMPLE_RATE") or 0.0
self.service = resolve_env_var_choice(
choice=service, env=os.getenv(constants.SERVICE_NAME_ENV, "service_undefined")
)
self.sampling_rate = resolve_env_var_choice(
choice=sampling_rate, env=os.getenv(constants.LOGGER_LOG_SAMPLING_RATE, 0.0)
)
self.log_level = self._get_log_level(level)
self.child = child
self._handler = logging.StreamHandler(stream) if stream is not None else logging.StreamHandler(sys.stdout)
Expand Down Expand Up @@ -193,7 +198,7 @@ def _configure_sampling(self):
f"Please review POWERTOOLS_LOGGER_SAMPLE_RATE environment variable."
)

def inject_lambda_context(self, lambda_handler: Callable[[Dict, Any], Any] = None, log_event: bool = False):
def inject_lambda_context(self, lambda_handler: Callable[[Dict, Any], Any] = None, log_event: bool = None):
"""Decorator to capture Lambda contextual info and inject into logger

Parameters
Expand Down Expand Up @@ -242,8 +247,9 @@ def handler(event, context):
logger.debug("Decorator called with parameters")
return functools.partial(self.inject_lambda_context, log_event=log_event)

log_event_env_option = str(os.getenv("POWERTOOLS_LOGGER_LOG_EVENT", "false"))
log_event = strtobool(log_event_env_option) or log_event
log_event = resolve_truthy_env_var_choice(
choice=log_event, env=os.getenv(constants.LOGGER_LOG_EVENT_ENV, "false")
)

@functools.wraps(lambda_handler)
def decorate(event, context):
Expand Down
6 changes: 4 additions & 2 deletions aws_lambda_powertools/metrics/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import fastjsonschema

from ..shared import constants
from ..shared.functions import resolve_env_var_choice
from .exceptions import MetricUnitError, MetricValueError, SchemaValidationError

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -88,8 +90,8 @@ def __init__(
):
self.metric_set = metric_set if metric_set is not None else {}
self.dimension_set = dimension_set if dimension_set is not None else {}
self.namespace = namespace or os.getenv("POWERTOOLS_METRICS_NAMESPACE")
self.service = service or os.environ.get("POWERTOOLS_SERVICE_NAME")
self.namespace = resolve_env_var_choice(choice=namespace, env=os.getenv(constants.METRICS_NAMESPACE_ENV))
self.service = resolve_env_var_choice(choice=service, env=os.getenv(constants.SERVICE_NAME_ENV))
self._metric_units = [unit.value for unit in MetricUnit]
self._metric_unit_options = list(MetricUnit.__members__)
self.metadata_set = self.metadata_set if metadata_set is not None else {}
Expand Down
9 changes: 6 additions & 3 deletions aws_lambda_powertools/middleware_factory/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
import inspect
import logging
import os
from distutils.util import strtobool
from typing import Callable

from ..shared import constants
from ..shared.functions import resolve_truthy_env_var_choice
from ..tracing import Tracer
from .exceptions import MiddlewareInvalidArgumentError

logger = logging.getLogger(__name__)


def lambda_handler_decorator(decorator: Callable = None, trace_execution=False):
def lambda_handler_decorator(decorator: Callable = None, trace_execution: bool = None):
"""Decorator factory for decorating Lambda handlers.

You can use lambda_handler_decorator to create your own middlewares,
Expand Down Expand Up @@ -104,7 +105,9 @@ def lambda_handler(event, context):
if decorator is None:
return functools.partial(lambda_handler_decorator, trace_execution=trace_execution)

trace_execution = trace_execution or strtobool(str(os.getenv("POWERTOOLS_TRACE_MIDDLEWARES", False)))
trace_execution = resolve_truthy_env_var_choice(
choice=trace_execution, env=os.getenv(constants.MIDDLEWARE_FACTORY_TRACE_ENV, "false")
)

@functools.wraps(decorator)
def final_decorator(func: Callable = None, **kwargs):
Expand Down
17 changes: 14 additions & 3 deletions aws_lambda_powertools/shared/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import os
TRACER_CAPTURE_RESPONSE_ENV: str = "POWERTOOLS_TRACER_CAPTURE_RESPONSE"
TRACER_CAPTURE_ERROR_ENV: str = "POWERTOOLS_TRACER_CAPTURE_ERROR"
TRACER_DISABLED_ENV: str = "POWERTOOLS_TRACE_DISABLED"

TRACER_CAPTURE_RESPONSE_ENV: str = os.getenv("POWERTOOLS_TRACER_CAPTURE_RESPONSE", "true")
TRACER_CAPTURE_ERROR_ENV: str = os.getenv("POWERTOOLS_TRACER_CAPTURE_ERROR", "true")
LOGGER_LOG_SAMPLING_RATE: str = "POWERTOOLS_LOGGER_SAMPLE_RATE"
LOGGER_LOG_EVENT_ENV: str = "POWERTOOLS_LOGGER_LOG_EVENT"

MIDDLEWARE_FACTORY_TRACE_ENV: str = "POWERTOOLS_TRACE_MIDDLEWARES"

METRICS_NAMESPACE_ENV: str = "POWERTOOLS_METRICS_NAMESPACE"

SAM_LOCAL_ENV: str = "AWS_SAM_LOCAL"
CHALICE_LOCAL_ENV: str = "AWS_CHALICE_CLI_MODE"
SERVICE_NAME_ENV: str = "POWERTOOLS_SERVICE_NAME"
XRAY_TRACE_ID_ENV: str = "_X_AMZN_TRACE_ID"
39 changes: 38 additions & 1 deletion aws_lambda_powertools/shared/functions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
from distutils.util import strtobool
from typing import Any, Union


def resolve_env_var_choice(env: str, choice: bool = None) -> bool:
def resolve_truthy_env_var_choice(env: Any, choice: bool = None) -> bool:
""" Pick explicit choice over truthy env value, if available, otherwise return truthy env value

NOTE: Environment variable should be resolved by the caller.

Parameters
----------
env : Any
environment variable actual value
choice : bool
explicit choice

Returns
-------
choice : str
resolved choice as either bool or environment value
"""
return choice if choice is not None else strtobool(env)


def resolve_env_var_choice(env: Any, choice: bool = None) -> Union[bool, Any]:
""" Pick explicit choice over env, if available, otherwise return env value received

NOTE: Environment variable should be resolved by the caller.

Parameters
----------
env : Any
environment variable actual value
choice : bool
explicit choice

Returns
-------
choice : str
resolved choice as either bool or environment value
"""
return choice if choice is not None else env
35 changes: 20 additions & 15 deletions aws_lambda_powertools/tracing/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
import inspect
import logging
import os
from distutils.util import strtobool
from typing import Any, Callable, Dict, List, Optional, Tuple

import aws_xray_sdk
import aws_xray_sdk.core

from aws_lambda_powertools.shared.constants import TRACER_CAPTURE_ERROR_ENV, TRACER_CAPTURE_RESPONSE_ENV
from aws_lambda_powertools.shared.functions import resolve_env_var_choice
from ..shared import constants
from ..shared.functions import resolve_truthy_env_var_choice

is_cold_start = True
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -283,9 +282,12 @@ def handler(event, context):
)

lambda_handler_name = lambda_handler.__name__

capture_response = resolve_env_var_choice(env=TRACER_CAPTURE_RESPONSE_ENV, choice=capture_response)
capture_error = resolve_env_var_choice(env=TRACER_CAPTURE_ERROR_ENV, choice=capture_error)
capture_response = resolve_truthy_env_var_choice(
env=os.getenv(constants.TRACER_CAPTURE_RESPONSE_ENV, "true"), choice=capture_response
)
capture_error = resolve_truthy_env_var_choice(
env=os.getenv(constants.TRACER_CAPTURE_ERROR_ENV, "true"), choice=capture_error
)

@functools.wraps(lambda_handler)
def decorate(event, context):
Expand Down Expand Up @@ -478,8 +480,12 @@ async def async_tasks():

method_name = f"{method.__name__}"

capture_response = resolve_env_var_choice(env=TRACER_CAPTURE_RESPONSE_ENV, choice=capture_response)
capture_error = resolve_env_var_choice(env=TRACER_CAPTURE_ERROR_ENV, choice=capture_error)
capture_response = resolve_truthy_env_var_choice(
env=os.getenv(constants.TRACER_CAPTURE_RESPONSE_ENV, "true"), choice=capture_response
)
capture_error = resolve_truthy_env_var_choice(
env=os.getenv(constants.TRACER_CAPTURE_ERROR_ENV, "true"), choice=capture_error
)

if inspect.iscoroutinefunction(method):
return self._decorate_async_function(
Expand Down Expand Up @@ -681,14 +687,13 @@ def _is_tracer_disabled() -> bool:
bool
"""
logger.debug("Verifying whether Tracing has been disabled")
is_lambda_sam_cli = os.getenv("AWS_SAM_LOCAL")
is_chalice_cli = os.getenv("AWS_CHALICE_CLI_MODE")
env_option = str(os.getenv("POWERTOOLS_TRACE_DISABLED", "false"))
disabled_env = strtobool(env_option)
is_lambda_sam_cli = os.getenv(constants.SAM_LOCAL_ENV)
is_chalice_cli = os.getenv(constants.CHALICE_LOCAL_ENV)
is_disabled = resolve_truthy_env_var_choice(env=os.getenv(constants.TRACER_DISABLED_ENV, "false"))

if disabled_env:
if is_disabled:
logger.debug("Tracing has been disabled via env var POWERTOOLS_TRACE_DISABLED")
return disabled_env
return is_disabled

if is_lambda_sam_cli or is_chalice_cli:
logger.debug("Running under SAM CLI env or not in Lambda env; disabling Tracing")
Expand All @@ -706,7 +711,7 @@ def __build_config(
):
""" Populates Tracer config for new and existing initializations """
is_disabled = disabled if disabled is not None else self._is_tracer_disabled()
is_service = service if service is not None else os.getenv("POWERTOOLS_SERVICE_NAME")
is_service = service if service is not None else os.getenv(constants.SERVICE_NAME_ENV)

self._config["provider"] = provider if provider is not None else self._config["provider"]
self._config["auto_patch"] = auto_patch if auto_patch is not None else self._config["auto_patch"]
Expand Down
6 changes: 3 additions & 3 deletions tests/functional/test_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ def test_logger_do_not_log_twice_when_root_logger_is_setup(stdout, service_name)

# WHEN we create a new Logger and child Logger
logger = Logger(service=service_name, stream=stdout)
child_logger = Logger(child=True, stream=stdout)
logger.info("hello")
child_logger.info("hello again")
child_logger = Logger(service=service_name, child=True, stream=stdout)
logger.info("PARENT")
child_logger.info("CHILD")

# THEN it should only contain only two log entries
# since child's log records propagated to root logger should be rejected
Expand Down
8 changes: 5 additions & 3 deletions tests/functional/test_shared_functions.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from aws_lambda_powertools.shared.functions import resolve_env_var_choice
from aws_lambda_powertools.shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice


def test_resolve_env_var_choice_explicit_wins_over_env_var():
assert resolve_env_var_choice(env="true", choice=False) is False
assert resolve_truthy_env_var_choice(env="true", choice=False) is False
assert resolve_env_var_choice(env="something", choice=False) is False


def test_resolve_env_var_choice_env_wins_over_absent_explicit():
assert resolve_env_var_choice(env="true") == 1
assert resolve_truthy_env_var_choice(env="true") == 1
assert resolve_env_var_choice(env="something") == "something"
41 changes: 36 additions & 5 deletions tests/unit/test_tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,11 +506,9 @@ def test_tracer_lambda_handler_override_response_as_metadata(mocker, provider_st
# GIVEN tracer is initialized
provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment)

mocker.patch("aws_lambda_powertools.tracing.tracer.TRACER_CAPTURE_RESPONSE_ENV", return_value=True)
tracer = Tracer(provider=provider, auto_patch=False)

# WHEN capture_lambda_handler decorator is used
# with capture_response set to False
# WHEN capture_lambda_handler decorator is used with capture_response set to False
@tracer.capture_lambda_handler(capture_response=False)
def handler(event, context):
return "response"
Expand All @@ -526,8 +524,7 @@ def test_tracer_method_override_response_as_metadata(provider_stub, in_subsegmen
provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment)
tracer = Tracer(provider=provider, auto_patch=False)

# WHEN capture_method decorator is used
# and the method response is empty
# WHEN capture_method decorator is used with capture_response set to False
@tracer.capture_method(capture_response=False)
def greeting(name, message):
return "response"
Expand All @@ -536,3 +533,37 @@ def greeting(name, message):

# THEN we should not add any metadata
assert in_subsegment_mock.put_metadata.call_count == 0


def test_tracer_lambda_handler_override_error_as_metadata(mocker, provider_stub, in_subsegment_mock):
# GIVEN tracer is initialized
provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment)
tracer = Tracer(provider=provider, auto_patch=False)

# WHEN capture_lambda_handler decorator is used with capture_error set to False
@tracer.capture_lambda_handler(capture_error=False)
def handler(event, context):
raise ValueError("error")

with pytest.raises(ValueError):
handler({}, mocker.MagicMock())

# THEN we should not add any metadata
assert in_subsegment_mock.put_metadata.call_count == 0


def test_tracer_method_override_error_as_metadata(provider_stub, in_subsegment_mock):
# GIVEN tracer is initialized
provider = provider_stub(in_subsegment=in_subsegment_mock.in_subsegment)
tracer = Tracer(provider=provider, auto_patch=False)

# WHEN capture_method decorator is used with capture_error set to False
@tracer.capture_method(capture_error=False)
def greeting(name, message):
raise ValueError("error")

with pytest.raises(ValueError):
greeting(name="Foo", message="Bar")

# THEN we should not add any metadata
assert in_subsegment_mock.put_metadata.call_count == 0