Skip to content

Commit 4bc8366

Browse files
committed
Submit metrics for invocations and errors
1 parent 5e37689 commit 4bc8366

File tree

4 files changed

+265
-25
lines changed

4 files changed

+265
-25
lines changed

datadog_lambda/tags.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
def parse_lambda_tags_from_arn(arn):
2+
"""Generate the list of lambda tags based on the data in the arn
3+
Args:
4+
arn (str): Lambda ARN.
5+
ex: arn:aws:lambda:us-east-1:123597598159:function:my-lambda[:optional-version]
6+
"""
7+
# Cap the number of times to split
8+
split_arn = arn.split(":")
9+
10+
# If ARN includes version / alias at the end, drop it
11+
if len(split_arn) > 7:
12+
split_arn = split_arn[:7]
13+
14+
_, _, _, region, account_id, _, function_name = split_arn
15+
16+
return [
17+
"region:{}".format(region),
18+
"account_id:{}".format(account_id),
19+
"functionname:{}".format(function_name),
20+
]
21+
22+
23+
def get_tags_from_context(context, cold_start_request_id):
24+
"""Uses properties of the Lambda context to create the list of tags
25+
26+
Args:
27+
context (dict<str, multiple types>): context this Lambda was invoked with
28+
cold_start_request_id (str): the first request ID to execute in this container
29+
30+
Returns:
31+
tag list (str[]): list of string tags in key:value format
32+
"""
33+
tags = parse_lambda_tags_from_arn(context.invoked_function_arn)
34+
tags.append("memorysize:{}".format(context.memory_limit_in_mb))
35+
36+
did_request_cold_start = cold_start_request_id == context.aws_request_id
37+
cold_start_tag = "cold_start:{}".format(str(did_request_cold_start).lower())
38+
tags.append(cold_start_tag)
39+
40+
return tags

datadog_lambda/wrapper.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@
77
import logging
88
import traceback
99

10-
from datadog_lambda.metric import lambda_stats
10+
from datadog_lambda.metric import lambda_stats, lambda_metric
1111
from datadog_lambda.patch import patch_all
12+
from datadog_lambda.tags import get_tags_from_context
1213
from datadog_lambda.tracing import (
1314
extract_dd_trace_context,
1415
set_correlation_ids,
1516
inject_correlation_ids,
1617
)
1718

19+
ENHANCED_METRICS_NAMESPACE_PREFIX = "aws.lambda.enhanced"
20+
1821
logger = logging.getLogger(__name__)
1922

2023

@@ -31,6 +34,10 @@ def my_lambda_handle(event, context):
3134
requests.get("https://www.datadoghq.com")
3235
"""
3336

37+
# On the first run of this Lambda container this variable is set
38+
# to the str value of the request ID, taken from the Lambda context
39+
cold_start_request_id = None
40+
3441

3542
class _LambdaDecorator(object):
3643
"""
@@ -40,19 +47,29 @@ class _LambdaDecorator(object):
4047

4148
def __init__(self, func):
4249
self.func = func
43-
self.flush_to_log = os.environ.get('DD_FLUSH_TO_LOG', '').lower() == 'true'
44-
self.logs_injection = os.environ.get('DD_LOGS_INJECTION', '').lower() == 'true'
50+
self.flush_to_log = os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true"
51+
self.logs_injection = os.environ.get("DD_LOGS_INJECTION", "").lower() == "true"
4552

4653
# Inject trace correlation ids to logs
4754
if self.logs_injection:
4855
inject_correlation_ids()
4956

5057
# Patch HTTP clients to propagate Datadog trace context
5158
patch_all()
52-
logger.debug('datadog_lambda_wrapper initialized')
59+
logger.debug("datadog_lambda_wrapper initialized")
5360

5461
def _before(self, event, context):
62+
global cold_start_request_id
63+
# Assign this request ID as the cold start if there is no value yet
64+
if cold_start_request_id is None:
65+
cold_start_request_id = context.aws_request_id
66+
5567
try:
68+
lambda_metric(
69+
"{}.invocations".format(ENHANCED_METRICS_NAMESPACE_PREFIX),
70+
1,
71+
tags=get_tags_from_context(context, cold_start_request_id),
72+
)
5673
# Extract Datadog trace context from incoming requests
5774
extract_dd_trace_context(event)
5875

@@ -72,6 +89,13 @@ def __call__(self, event, context):
7289
self._before(event, context)
7390
try:
7491
return self.func(event, context)
92+
except Exception:
93+
lambda_metric(
94+
"{}.errors".format(ENHANCED_METRICS_NAMESPACE_PREFIX),
95+
1,
96+
tags=get_tags_from_context(context, cold_start_request_id),
97+
)
98+
raise
7599
finally:
76100
self._after(event, context)
77101

tests/test_tags.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import unittest
2+
3+
from datadog_lambda.tags import parse_lambda_tags_from_arn, get_tags_from_context
4+
from tests.test_wrapper import get_mock_context
5+
6+
7+
class TestMetricTags(unittest.TestCase):
8+
def test_parse_lambda_tags_from_arn(self):
9+
self.assertListEqual(
10+
parse_lambda_tags_from_arn(
11+
"arn:aws:lambda:us-east-1:1234597598159:function:swf-hello-test"
12+
),
13+
[
14+
"region:us-east-1",
15+
"account_id:1234597598159",
16+
"functionname:swf-hello-test",
17+
],
18+
)
19+
20+
self.assertListEqual(
21+
parse_lambda_tags_from_arn(
22+
"arn:aws:lambda:us-west-1:1234597598159:function:other-function:function-alias"
23+
),
24+
[
25+
"region:us-west-1",
26+
"account_id:1234597598159",
27+
"functionname:other-function",
28+
],
29+
)
30+
31+
def test_get_tags_from_context(self):
32+
cold_start_request_id = "first-request-id"
33+
self.assertListEqual(
34+
get_tags_from_context(
35+
get_mock_context(aws_request_id=cold_start_request_id),
36+
cold_start_request_id,
37+
),
38+
[
39+
"region:us-west-1",
40+
"account_id:123457598159",
41+
"functionname:python-layer-test",
42+
"memorysize:256",
43+
"cold_start:true",
44+
],
45+
)
46+
47+
self.assertListEqual(
48+
get_tags_from_context(
49+
get_mock_context(aws_request_id="non-cold-start-request-id"),
50+
cold_start_request_id,
51+
),
52+
[
53+
"region:us-west-1",
54+
"account_id:123457598159",
55+
"functionname:python-layer-test",
56+
"memorysize:256",
57+
"cold_start:false",
58+
],
59+
)
60+

0 commit comments

Comments
 (0)