From 191710f3b5fc19f895e6346853d886c789e80b54 Mon Sep 17 00:00:00 2001 From: Stephen Firrincieli Date: Thu, 31 Oct 2019 16:28:14 -0400 Subject: [PATCH 1/3] Tag enhanced metrics with runtime and memorysize --- datadog_lambda/metric.py | 11 +++++------ datadog_lambda/tags.py | 25 +++++++++++++++++++++++++ datadog_lambda/wrapper.py | 4 ++-- tests/test_metric.py | 31 +++++++++++++++---------------- tests/test_tags.py | 19 ++++++++++++++++++- tests/test_wrapper.py | 25 ++++++++++++++++++++----- 6 files changed, 85 insertions(+), 30 deletions(-) diff --git a/datadog_lambda/metric.py b/datadog_lambda/metric.py index 784b4286..3a1997e8 100644 --- a/datadog_lambda/metric.py +++ b/datadog_lambda/metric.py @@ -14,8 +14,7 @@ from datadog import api from datadog.threadstats import ThreadStats from datadog_lambda import __version__ -from datadog_lambda.cold_start import get_cold_start_tag -from datadog_lambda.tags import parse_lambda_tags_from_arn +from datadog_lambda.tags import get_enhanced_metrics_tags ENHANCED_METRICS_NAMESPACE_PREFIX = "aws.lambda.enhanced" @@ -82,7 +81,7 @@ def are_enhanced_metrics_enabled(): return os.environ.get("DD_ENHANCED_METRICS", "false").lower() == "true" -def submit_invocations_metric(lambda_arn): +def submit_invocations_metric(lambda_context): """Increment aws.lambda.enhanced.invocations by 1 """ if not are_enhanced_metrics_enabled(): @@ -91,11 +90,11 @@ def submit_invocations_metric(lambda_arn): lambda_metric( "{}.invocations".format(ENHANCED_METRICS_NAMESPACE_PREFIX), 1, - tags=parse_lambda_tags_from_arn(lambda_arn) + [get_cold_start_tag()], + tags=get_enhanced_metrics_tags(lambda_context), ) -def submit_errors_metric(lambda_arn): +def submit_errors_metric(lambda_context): """Increment aws.lambda.enhanced.errors by 1 """ if not are_enhanced_metrics_enabled(): @@ -104,7 +103,7 @@ def submit_errors_metric(lambda_arn): lambda_metric( "{}.errors".format(ENHANCED_METRICS_NAMESPACE_PREFIX), 1, - tags=parse_lambda_tags_from_arn(lambda_arn) + [get_cold_start_tag()], + tags=get_enhanced_metrics_tags(lambda_context), ) diff --git a/datadog_lambda/tags.py b/datadog_lambda/tags.py index 4d962257..fa9f8ec2 100644 --- a/datadog_lambda/tags.py +++ b/datadog_lambda/tags.py @@ -1,3 +1,8 @@ +from platform import python_version_tuple + +from datadog_lambda.cold_start import get_cold_start_tag + + def parse_lambda_tags_from_arn(arn): """Generate the list of lambda tags based on the data in the arn Args: @@ -18,3 +23,23 @@ def parse_lambda_tags_from_arn(arn): "account_id:{}".format(account_id), "functionname:{}".format(function_name), ] + + +def get_runtime_tag(): + """Get the runtime tag from the current Python version + """ + major_version, minor_version, _ = python_version_tuple() + + return "runtime:python{major}.{minor}".format( + major=major_version, minor=minor_version + ) + + +def get_enhanced_metrics_tags(lambda_context): + """Get the list of tags to apply to enhanced metrics + """ + return parse_lambda_tags_from_arn(lambda_context.invoked_function_arn) + [ + get_cold_start_tag(), + "memorysize:{}".format(lambda_context.memory_limit_in_mb), + get_runtime_tag(), + ] diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index a2c1fad4..99678ffa 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -61,7 +61,7 @@ def _before(self, event, context): set_cold_start() try: - submit_invocations_metric(context.invoked_function_arn) + submit_invocations_metric(context) # Extract Datadog trace context from incoming requests extract_dd_trace_context(event) @@ -82,7 +82,7 @@ def __call__(self, event, context): try: return self.func(event, context) except Exception: - submit_errors_metric(context.invoked_function_arn) + submit_errors_metric(context) raise finally: self._after(event, context) diff --git a/tests/test_metric.py b/tests/test_metric.py index 7e553ae8..0329be38 100644 --- a/tests/test_metric.py +++ b/tests/test_metric.py @@ -1,38 +1,37 @@ import os import unittest + try: from unittest.mock import patch, call except ImportError: from mock import patch, call -from datadog_lambda.metric import ( - lambda_metric, - _format_dd_lambda_layer_tag, -) +from datadog_lambda.metric import lambda_metric, _format_dd_lambda_layer_tag class TestLambdaMetric(unittest.TestCase): - def setUp(self): - patcher = patch('datadog_lambda.metric.lambda_stats') + patcher = patch("datadog_lambda.metric.lambda_stats") self.mock_metric_lambda_stats = patcher.start() self.addCleanup(patcher.stop) def test_lambda_metric_tagged_with_dd_lambda_layer(self): - lambda_metric('test', 1) - lambda_metric('test', 1, 123, []) - lambda_metric('test', 1, tags=['tag1:test']) + lambda_metric("test", 1) + lambda_metric("test", 1, 123, []) + lambda_metric("test", 1, tags=["tag1:test"]) expected_tag = _format_dd_lambda_layer_tag() - self.mock_metric_lambda_stats.distribution.assert_has_calls([ - call('test', 1, timestamp=None, tags=[expected_tag]), - call('test', 1, timestamp=123, tags=[expected_tag]), - call('test', 1, timestamp=None, tags=['tag1:test', expected_tag]), - ]) + self.mock_metric_lambda_stats.distribution.assert_has_calls( + [ + call("test", 1, timestamp=None, tags=[expected_tag]), + call("test", 1, timestamp=123, tags=[expected_tag]), + call("test", 1, timestamp=None, tags=["tag1:test", expected_tag]), + ] + ) def test_lambda_metric_flush_to_log(self): - os.environ["DD_FLUSH_TO_LOG"] = 'True' + os.environ["DD_FLUSH_TO_LOG"] = "True" - lambda_metric('test', 1) + lambda_metric("test", 1) self.mock_metric_lambda_stats.distribution.assert_not_called() del os.environ["DD_FLUSH_TO_LOG"] diff --git a/tests/test_tags.py b/tests/test_tags.py index f3dfc4cf..af4c8882 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -1,9 +1,19 @@ import unittest -from datadog_lambda.tags import parse_lambda_tags_from_arn +try: + from unittest.mock import patch +except ImportError: + from mock import patch + +from datadog_lambda.tags import parse_lambda_tags_from_arn, get_runtime_tag class TestMetricTags(unittest.TestCase): + def setUp(self): + patcher = patch("datadog_lambda.tags.python_version_tuple") + self.mock_python_version_tuple = patcher.start() + self.addCleanup(patcher.stop) + def test_parse_lambda_tags_from_arn(self): self.assertListEqual( parse_lambda_tags_from_arn( @@ -27,3 +37,10 @@ def test_parse_lambda_tags_from_arn(self): ], ) + def test_get_runtime_tag(self): + self.mock_python_version_tuple.return_value = ("2", "7", "10") + self.assertEqual(get_runtime_tag(), "runtime:python2.7") + + self.mock_python_version_tuple.return_value = ("3", "7", "2") + self.assertEqual(get_runtime_tag(), "runtime:python3.7") + diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 4be85b5c..bcb69a86 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -33,7 +33,7 @@ def setUp(self): self.addCleanup(patcher.stop) patcher = patch("datadog_lambda.metric.lambda_metric") - self.mock_wrapper_lambda_metric = patcher.start() + self.mock_lambda_metric = patcher.start() self.addCleanup(patcher.stop) patcher = patch("datadog_lambda.wrapper.extract_dd_trace_context") @@ -57,6 +57,11 @@ def setUp(self): self.mock_is_cold_start.return_value = True self.addCleanup(patcher.stop) + patcher = patch("datadog_lambda.tags.python_version_tuple") + self.mock_python_version_tuple = patcher.start() + self.mock_python_version_tuple.return_value = ("2", "7", "10") + self.addCleanup(patcher.stop) + def test_datadog_lambda_wrapper(self): @datadog_lambda_wrapper def lambda_handler(event, context): @@ -116,7 +121,7 @@ def lambda_handler(event, context): lambda_handler(lambda_event, get_mock_context()) - self.mock_wrapper_lambda_metric.assert_has_calls( + self.mock_lambda_metric.assert_has_calls( [ call( "aws.lambda.enhanced.invocations", @@ -126,6 +131,8 @@ def lambda_handler(event, context): "account_id:123457598159", "functionname:python-layer-test", "cold_start:true", + "memorysize:256", + "runtime:python2.7", ], ) ] @@ -145,7 +152,7 @@ def lambda_handler(event, context): with self.assertRaises(RuntimeError): lambda_handler(lambda_event, get_mock_context()) - self.mock_wrapper_lambda_metric.assert_has_calls( + self.mock_lambda_metric.assert_has_calls( [ call( "aws.lambda.enhanced.invocations", @@ -155,6 +162,8 @@ def lambda_handler(event, context): "account_id:123457598159", "functionname:python-layer-test", "cold_start:true", + "memorysize:256", + "runtime:python2.7", ], ), call( @@ -165,6 +174,8 @@ def lambda_handler(event, context): "account_id:123457598159", "functionname:python-layer-test", "cold_start:true", + "memorysize:256", + "runtime:python2.7", ], ), ] @@ -189,7 +200,7 @@ def lambda_handler(event, context): lambda_event, get_mock_context(aws_request_id="second-request-id") ) - self.mock_wrapper_lambda_metric.assert_has_calls( + self.mock_lambda_metric.assert_has_calls( [ call( "aws.lambda.enhanced.invocations", @@ -199,6 +210,8 @@ def lambda_handler(event, context): "account_id:123457598159", "functionname:python-layer-test", "cold_start:true", + "memorysize:256", + "runtime:python2.7", ], ), call( @@ -209,6 +222,8 @@ def lambda_handler(event, context): "account_id:123457598159", "functionname:python-layer-test", "cold_start:false", + "memorysize:256", + "runtime:python2.7", ], ), ] @@ -226,5 +241,5 @@ def lambda_handler(event, context): with self.assertRaises(RuntimeError): lambda_handler(lambda_event, get_mock_context()) - self.mock_wrapper_lambda_metric.assert_not_called() + self.mock_lambda_metric.assert_not_called() From 2d12a5b7ed7bb3c76e281a3ed68427dd65e76e02 Mon Sep 17 00:00:00 2001 From: Stephen Firrincieli Date: Thu, 31 Oct 2019 17:45:50 -0400 Subject: [PATCH 2/3] Check out tests/test_metric.py from master --- tests/test_metric.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/test_metric.py b/tests/test_metric.py index 0329be38..7e553ae8 100644 --- a/tests/test_metric.py +++ b/tests/test_metric.py @@ -1,37 +1,38 @@ import os import unittest - try: from unittest.mock import patch, call except ImportError: from mock import patch, call -from datadog_lambda.metric import lambda_metric, _format_dd_lambda_layer_tag +from datadog_lambda.metric import ( + lambda_metric, + _format_dd_lambda_layer_tag, +) class TestLambdaMetric(unittest.TestCase): + def setUp(self): - patcher = patch("datadog_lambda.metric.lambda_stats") + patcher = patch('datadog_lambda.metric.lambda_stats') self.mock_metric_lambda_stats = patcher.start() self.addCleanup(patcher.stop) def test_lambda_metric_tagged_with_dd_lambda_layer(self): - lambda_metric("test", 1) - lambda_metric("test", 1, 123, []) - lambda_metric("test", 1, tags=["tag1:test"]) + lambda_metric('test', 1) + lambda_metric('test', 1, 123, []) + lambda_metric('test', 1, tags=['tag1:test']) expected_tag = _format_dd_lambda_layer_tag() - self.mock_metric_lambda_stats.distribution.assert_has_calls( - [ - call("test", 1, timestamp=None, tags=[expected_tag]), - call("test", 1, timestamp=123, tags=[expected_tag]), - call("test", 1, timestamp=None, tags=["tag1:test", expected_tag]), - ] - ) + self.mock_metric_lambda_stats.distribution.assert_has_calls([ + call('test', 1, timestamp=None, tags=[expected_tag]), + call('test', 1, timestamp=123, tags=[expected_tag]), + call('test', 1, timestamp=None, tags=['tag1:test', expected_tag]), + ]) def test_lambda_metric_flush_to_log(self): - os.environ["DD_FLUSH_TO_LOG"] = "True" + os.environ["DD_FLUSH_TO_LOG"] = 'True' - lambda_metric("test", 1) + lambda_metric('test', 1) self.mock_metric_lambda_stats.distribution.assert_not_called() del os.environ["DD_FLUSH_TO_LOG"] From 141325a609115bfa11817f990094d47c9b7c3203 Mon Sep 17 00:00:00 2001 From: Stephen Firrincieli Date: Mon, 4 Nov 2019 11:51:33 -0500 Subject: [PATCH 3/3] Bump version and update changelog --- CHANGELOG.md | 4 ++++ datadog_lambda/__init__.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a658e399..f38a01d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +# Version 9 / 2019-11-04 + +- Tag layer-generated `aws.lambda.enhanced.invocations` and `aws.lambda.enhanced.errors` enhanced metrics with `runtime` and `memorysize` + # Version 8 / 2019-10-24 - Remove vendored botocore requests patching since the package has been removed from the latest botocore diff --git a/datadog_lambda/__init__.py b/datadog_lambda/__init__.py index 0edd20bc..16d81390 100644 --- a/datadog_lambda/__init__.py +++ b/datadog_lambda/__init__.py @@ -1,6 +1,6 @@ # The minor version corresponds to the Lambda layer version. # E.g.,, version 0.5.0 gets packaged into layer version 5. -__version__ = '0.8.0' +__version__ = '0.9.0' import os