forked from DataDog/datadogpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaws_lambda.py
100 lines (78 loc) · 3.29 KB
/
aws_lambda.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# Unless explicitly stated otherwise all files in this repository are licensed under the BSD-3-Clause License.
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2015-Present Datadog, Inc
from datadog.threadstats import ThreadStats
from threading import Lock, Thread
from datadog import api
import os
import warnings
"""
DEPRECATED use datadog-lambda package instead https://git.io/fjy8o
Usage:
from datadog import datadog_lambda_wrapper, lambda_metric
@datadog_lambda_wrapper
def my_lambda_handle(event, context):
lambda_metric("some_metric", 10)
"""
class _LambdaDecorator(object):
""" DEPRECATED Decorator to automatically init & flush metrics, created for Lambda functions"""
# Number of opened wrappers, flush when 0
_counter = 0
_counter_lock = Lock()
_flush_lock = Lock()
_was_initialized = False
def __init__(self, func):
self.func = func
@classmethod
def _enter(cls):
with cls._counter_lock:
if not cls._was_initialized:
cls._was_initialized = True
api._api_key = os.environ.get('DATADOG_API_KEY', os.environ.get('DD_API_KEY'))
api._api_host = os.environ.get('DATADOG_HOST', 'https://api.datadoghq.com')
# Async initialization of the TLS connection with our endpoints
# This avoids adding execution time at the end of the lambda run
t = Thread(target=_init_api_client)
t.start()
cls._counter = cls._counter + 1
@classmethod
def _close(cls):
should_flush = False
with cls._counter_lock:
cls._counter = cls._counter - 1
# Flush only when all wrappers are closed
if cls._counter <= 0:
should_flush = True
if should_flush:
with cls._flush_lock:
# Don't flush if other wrappers were opened while _flush_lock was locked
with cls._counter_lock:
if cls._counter > 0:
should_flush = False
if should_flush:
_lambda_stats.flush(float("inf"))
def __call__(self, *args, **kw):
warnings.warn("datadog_lambda_wrapper() is relocated to https://git.io/fjy8o", DeprecationWarning)
_LambdaDecorator._enter()
try:
return self.func(*args, **kw)
finally:
_LambdaDecorator._close()
_lambda_stats = ThreadStats()
_lambda_stats.start(flush_in_greenlet=False, flush_in_thread=False)
datadog_lambda_wrapper = _LambdaDecorator
def lambda_metric(*args, **kw):
""" Alias to expose only distributions for lambda functions"""
_lambda_stats.distribution(*args, **kw)
def _init_api_client():
""" No-op GET to initialize the requests connection with DD's endpoints
The goal here is to make the final flush faster:
we keep alive the Requests session, this means that we can re-use the connection
The consequence is that the HTTP Handshake, which can take hundreds of ms,
is now made at the beginning of a lambda instead of at the end.
By making the initial request async, we spare a lot of execution time in the lambdas.
"""
try:
api.api_client.APIClient.submit('GET', 'validate')
except Exception:
pass