def test_incorrect_metric_unit(metric, dimension, namespace): metric["unit"] = "incorrect_unit" with pytest.raises(MetricUnitError): with single_metric(**metric) as m: m.add_dimension(**dimension) m.add_namespace(**namespace)
def lambda_handler(event, context): """Sample pure Lambda function Parameters ---------- event: dict, required API Gateway Lambda Proxy Input Format Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format context: object, required Lambda Context runtime methods and attributes Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html Returns ------ API Gateway Lambda Proxy Output Format: dict Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html """ sums_values() async_http_ret = asyncio.run(async_tasks()) if "charge_id" in event: logger.structure_logs(append=True, payment_id="charge_id") global _cold_start if _cold_start: logger.debug("Recording cold start metric") metrics.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1) metrics.add_dimension(name="function_name", value=context.function_name) _cold_start = False try: ip = requests.get("http://checkip.amazonaws.com/") metrics.add_metric(name="SuccessfulLocations", unit="Count", value=1) except requests.RequestException as e: # Send some context about this error to Lambda Logs logger.exception(e, exc_info=True) raise with single_metric(name="UniqueMetricDimension", unit="Seconds", value=1) as metric: metric.add_dimension(name="unique_dimension", value="for_unique_metric") resp = { "message": "hello world", "location": ip.text.replace("\n", ""), "async_http": async_http_ret } logger.info("Returning message to the caller") return { "statusCode": 200, "body": json.dumps(resp), }
def test_schema_no_namespace(metric, dimension): # GIVEN we don't add any metric or dimension # but a namespace # WHEN we attempt to serialize a valid EMF object # THEN it should fail validation and raise SchemaValidationError with pytest.raises(SchemaValidationError): with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension)
def test_multiple_namespaces(metric, dimension, namespace): namespace_a = {"name": "OtherNamespace"} namespace_b = {"name": "AnotherNamespace"} with pytest.raises(UniqueNamespaceError): with single_metric(**metric) as m: m.add_dimension(**dimension) m.add_namespace(**namespace) m.add_namespace(**namespace_a) m.add_namespace(**namespace_b)
def test_schema_incorrect_value(metric, dimension, namespace): # GIVEN we pass an incorrect metric value (non-number/float) metric["value"] = "some_value" # WHEN we attempt to serialize a valid EMF object # THEN it should fail validation and raise SchemaValidationError with pytest.raises(MetricValueError): with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace)
def test_incorrect_metric_unit(metric, dimension, namespace): # GIVEN we pass a metric unit not supported by CloudWatch metric["unit"] = "incorrect_unit" # WHEN we attempt to add a new metric # THEN it should fail validation and raise MetricUnitError with pytest.raises(MetricUnitError): with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace)
def test_exceed_number_of_dimensions(metric, namespace): dimensions = [] for i in range(11): dimensions.append({"name": f"test_{i}", "value": "test"}) with pytest.raises(SchemaValidationError): with single_metric(**metric) as m: m.add_namespace(**namespace) for dimension in dimensions: m.add_dimension(**dimension)
def test_single_metric(capsys, metric, dimension, namespace): with single_metric(**metric) as my_metrics: my_metrics.add_dimension(**dimension) my_metrics.add_namespace(**namespace) output = json.loads(capsys.readouterr().out.strip()) expected = serialize_single_metric(metric=metric, dimension=dimension, namespace=namespace) remove_timestamp(metrics=[output, expected]) # Timestamp will always be different assert expected["_aws"] == output["_aws"]
def test_single_metric_one_metric_only(capsys, metric, dimension, namespace): with single_metric(**metric) as my_metric: my_metric.add_metric(name="second_metric", unit="Count", value=1) my_metric.add_metric(name="third_metric", unit="Seconds", value=1) my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace) output = json.loads(capsys.readouterr().out.strip()) expected = serialize_single_metric(metric=metric, dimension=dimension, namespace=namespace) remove_timestamp(metrics=[output, expected]) # Timestamp will always be different assert expected["_aws"] == output["_aws"]
def test_namespace_env_var(monkeypatch, capsys, metric, dimension, namespace): monkeypatch.setenv("POWERTOOLS_METRICS_NAMESPACE", namespace["name"]) with single_metric(**metric) as my_metrics: my_metrics.add_dimension(**dimension) monkeypatch.delenv("POWERTOOLS_METRICS_NAMESPACE") output = json.loads(capsys.readouterr().out.strip()) expected = serialize_single_metric(metric=metric, dimension=dimension, namespace=namespace) remove_timestamp(metrics=[output, expected]) # Timestamp will always be different assert expected["_aws"] == output["_aws"]
def test_all_possible_metric_units(metric, dimension, namespace): # GIVEN we add a metric for each metric unit supported by CloudWatch # where metric unit as MetricUnit key e.g. "Seconds", "BytesPerSecond" for unit in MetricUnit: metric["unit"] = unit.name # WHEN we iterate over all available metric unit keys from MetricUnit enum # THEN we raise no MetricUnitError nor SchemaValidationError with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace) # WHEN we iterate over all available metric unit keys from MetricUnit enum all_metric_units = [unit.value for unit in MetricUnit] # metric unit as MetricUnit value e.g. "Seconds", "Bytes/Second" for unit in all_metric_units: metric["unit"] = unit # THEN we raise no MetricUnitError nor SchemaValidationError with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace)
def test_multiple_namespaces_exception(metric, dimension, namespace): # GIVEN we attempt to add multiple namespaces namespace_a = {"name": "OtherNamespace"} namespace_b = {"name": "AnotherNamespace"} # WHEN an EMF object can only have one # THEN we should raise UniqueNamespaceError exception with pytest.raises(UniqueNamespaceError): with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace) my_metric.add_namespace(**namespace_a) my_metric.add_namespace(**namespace_b)
def test_exceed_number_of_dimensions(metric, namespace): # GIVEN we we have more dimensions than CloudWatch supports dimensions = [] for i in range(11): dimensions.append({"name": f"test_{i}", "value": "test"}) # WHEN we attempt to serialize them into a valid EMF object # THEN it should fail validation and raise SchemaValidationError with pytest.raises(SchemaValidationError): with single_metric(**metric) as my_metric: my_metric.add_namespace(**namespace) for dimension in dimensions: my_metric.add_dimension(**dimension)
def test_all_metric_units_string(metric, dimension, namespace): # metric unit as MetricUnit key e.g. "Seconds", "BytesPerSecond" for unit in MetricUnit: metric["unit"] = unit.name with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace) with pytest.raises(MetricUnitError): metric["unit"] = "seconds" with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace) all_metric_units = [unit.value for unit in MetricUnit] # metric unit as MetricUnit value e.g. "Seconds", "Bytes/Second" for unit in all_metric_units: metric["unit"] = unit with single_metric(**metric) as my_metric: my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace)
def test_single_metric_one_metric_only(capsys, metric, dimension, namespace): # GIVEN we attempt to add more than one metric # WHEN using single_metric context manager with single_metric(**metric) as my_metric: my_metric.add_metric(name="second_metric", unit="Count", value=1) my_metric.add_metric(name="third_metric", unit="Seconds", value=1) my_metric.add_dimension(**dimension) my_metric.add_namespace(**namespace) output = json.loads(capsys.readouterr().out.strip()) expected = serialize_single_metric(metric=metric, dimension=dimension, namespace=namespace) remove_timestamp(metrics=[output, expected]) # Timestamp will always be different # THEN we should only have the first metric added assert expected["_aws"] == output["_aws"]
def test_namespace_env_var(monkeypatch, capsys, metric, dimension, namespace): # GIVEN we use POWERTOOLS_METRICS_NAMESPACE monkeypatch.setenv("POWERTOOLS_METRICS_NAMESPACE", namespace["name"]) # WHEN creating a metric but don't explicitly # add a namespace with single_metric(**metric) as my_metrics: my_metrics.add_dimension(**dimension) monkeypatch.delenv("POWERTOOLS_METRICS_NAMESPACE") output = json.loads(capsys.readouterr().out.strip()) expected = serialize_single_metric(metric=metric, dimension=dimension, namespace=namespace) remove_timestamp(metrics=[output, expected]) # Timestamp will always be different # THEN we should add a namespace implicitly # with the value of POWERTOOLS_METRICS_NAMESPACE env var assert expected["_aws"] == output["_aws"]
def test_schema_incorrect_value(metric, dimension, namespace): metric["value"] = "some_value" with pytest.raises(MetricValueError): with single_metric(**metric) as m: m.add_dimension(**dimension) m.add_namespace(**namespace)
def test_schema_no_namespace(metric, dimension): with pytest.raises(SchemaValidationError): with single_metric(**metric) as m: m.add_dimension(**dimension)