-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #324 from DataDog/quentin.pierre/aws-lambda-decorator
Add decorator for AWS lambda
- Loading branch information
Showing
5 changed files
with
162 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
from datadog.threadstats.base import ThreadStats # noqa | ||
from datadog.threadstats.aws_lambda import lambda_metric, datadog_lambda_wrapper # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
from datadog.threadstats import ThreadStats | ||
from threading import Lock | ||
from datadog import api | ||
import os | ||
|
||
|
||
""" | ||
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): | ||
""" 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') | ||
api._api_host = os.environ.get('DATADOG_HOST', 'https://api.datadoghq.com') | ||
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): | ||
_LambdaDecorator._enter() | ||
result = self.func(*args, **kw) | ||
_LambdaDecorator._close() | ||
return result | ||
|
||
|
||
_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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import time | ||
# import unittest | ||
import threading | ||
|
||
from datadog import lambda_metric, datadog_lambda_wrapper | ||
from datadog.threadstats.aws_lambda import _lambda_stats | ||
|
||
|
||
TOTAL_NUMBER_OF_THREADS = 1000 | ||
|
||
|
||
class MemoryReporter(object): | ||
""" A reporting class that reports to memory for testing. """ | ||
|
||
def __init__(self): | ||
self.distributions = [] | ||
self.dist_flush_counter = 0 | ||
|
||
def flush_distributions(self, dists): | ||
self.distributions += dists | ||
self.dist_flush_counter = self.dist_flush_counter + 1 | ||
|
||
|
||
@datadog_lambda_wrapper | ||
def wrapped_function(id): | ||
lambda_metric("dist_" + str(id), 42) | ||
# sleep makes the os continue another thread | ||
time.sleep(0.001) | ||
|
||
lambda_metric("common_dist", 42) | ||
|
||
|
||
class TestWrapperThreadSafety(object): | ||
|
||
def test_wrapper_thread_safety(self): | ||
_lambda_stats.reporter = MemoryReporter() | ||
|
||
for i in range(TOTAL_NUMBER_OF_THREADS): | ||
threading.Thread(target=wrapped_function, args=[i]).start() | ||
# Wait all threads to finish | ||
time.sleep(10) | ||
|
||
# Check that at least one flush happened | ||
self.assertGreater(_lambda_stats.reporter.dist_flush_counter, 0) | ||
|
||
dists = _lambda_stats.reporter.distributions | ||
self.assertEqual(len(dists), TOTAL_NUMBER_OF_THREADS + 1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters