summaryrefslogtreecommitdiff
path: root/raven/contrib
diff options
context:
space:
mode:
authorAshley Camba <ashwoods@gmail.com>2017-10-13 11:56:10 +0200
committerGitHub <noreply@github.com>2017-10-13 11:56:10 +0200
commite1fd30be2af23f049f6ccdcdb81061e7359f99dc (patch)
treee26d860e9e73d29954a9f14a5bcd7826e71ffc4a /raven/contrib
parent608d34ebb811dbaa8f013936b6f0579201a57301 (diff)
downloadraven-e1fd30be2af23f049f6ccdcdb81061e7359f99dc.tar.gz
AWS Lambda Integration (#1107)
* Initial lambda integration * feature(lambda): Add lambda tests and client * Add D107 to ignore flake8 docs * Initial lambda integration * feature(lambda): Add lambda tests and client * Add reference to raven-python-lambda
Diffstat (limited to 'raven/contrib')
-rw-r--r--raven/contrib/awslambda/__init__.py173
1 files changed, 173 insertions, 0 deletions
diff --git a/raven/contrib/awslambda/__init__.py b/raven/contrib/awslambda/__init__.py
new file mode 100644
index 0000000..7d6f2b8
--- /dev/null
+++ b/raven/contrib/awslambda/__init__.py
@@ -0,0 +1,173 @@
+"""
+raven.contrib.awslambda
+~~~~~~~~~~~~~~~~~~~~
+
+Raven wrapper for AWS Lambda handlers.
+
+:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
+:license: BSD, see LICENSE for more details.
+"""
+# flake8: noqa
+
+from __future__ import absolute_import
+
+import os
+import logging
+import functools
+from types import FunctionType
+
+from raven.base import Client
+from raven.transport.http import HTTPTransport
+
+logger = logging.getLogger('sentry.errors.client')
+
+
+def get_default_tags():
+ return {
+ 'lambda': 'AWS_LAMBDA_FUNCTION_NAME',
+ 'version': 'AWS_LAMBDA_FUNCTION_VERSION',
+ 'memory_size': 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE',
+ 'log_group': 'AWS_LAMBDA_LOG_GROUP_NAME',
+ 'log_stream': 'AWS_LAMBDA_LOG_STREAM_NAME',
+ 'region': 'AWS_REGION'
+ }
+
+
+class LambdaClient(Client):
+ """
+ Raven decorator for AWS Lambda.
+
+ By default, the lambda integration will capture unhandled exceptions and instrument logging.
+
+ Usage:
+
+ >>> from raven.contrib.awslambda import LambdaClient
+ >>>
+ >>>
+ >>> client = LambdaClient()
+ >>>
+ >>> @client.capture_exceptions
+ >>> def handler(event, context):
+ >>> ...
+ >>> raise Exception('I will be sent to sentry!')
+
+ """
+
+ def __init__(self, *args, **kwargs):
+ transport = kwargs.get('transport', HTTPTransport)
+ super(LambdaClient, self).__init__(*args, transport=transport, **kwargs)
+
+ def capture(self, *args, **kwargs):
+ if 'data' not in kwargs:
+ kwargs['data'] = data = {}
+ else:
+ data = kwargs['data']
+ event = kwargs.get('event', None)
+ context = kwargs.get('context', None)
+ user_info = self._get_user_interface(event)
+ if user_info:
+ data.update(user_info)
+ if event:
+ http_info = self._get_http_interface(event)
+ if http_info:
+ data.update(http_info)
+ data['extra'] = self._get_extra_data(event, context)
+ return super(LambdaClient, self).capture(*args, **kwargs)
+
+ def build_msg(self, *args, **kwargs):
+
+ data = super(LambdaClient, self).build_msg(*args, **kwargs)
+ for option, default in get_default_tags().items():
+ data['tags'].setdefault(option, os.environ.get(default))
+ data.setdefault('release', os.environ.get('SENTRY_RELEASE'))
+ data.setdefault('environment', os.environ.get('SENTRY_ENVIRONMENT'))
+ return data
+
+ def capture_exceptions(self, f=None, exceptions=None): # TODO: Ash fix kwargs in base
+ """
+ Wrap a function or code block in try/except and automatically call
+ ``.captureException`` if it raises an exception, then the exception
+ is reraised.
+
+ By default, it will capture ``Exception``
+
+ >>> @client.capture_exceptions
+ >>> def foo():
+ >>> raise Exception()
+
+ >>> with client.capture_exceptions():
+ >>> raise Exception()
+
+ You can also specify exceptions to be caught specifically
+
+ >>> @client.capture_exceptions((IOError, LookupError))
+ >>> def bar():
+ >>> ...
+
+ ``kwargs`` are passed through to ``.captureException``.
+ """
+ if not isinstance(f, FunctionType):
+ # when the decorator has args which is not a function we except
+ # f to be the exceptions tuple
+ return functools.partial(self.capture_exceptions, exceptions=f)
+
+ exceptions = exceptions or (Exception,)
+
+ @functools.wraps(f)
+ def wrapped(event, context, *args, **kwargs):
+ try:
+ return f(event, context, *args, **kwargs)
+ except exceptions:
+ self.captureException(event=event, context=context, **kwargs)
+ self.context.clear()
+ raise
+ return wrapped
+
+ @staticmethod
+ def _get_user_interface(event):
+ if event.get('requestContext'):
+ identity = event['requestContext']['identity']
+ if identity:
+ user = {
+ 'id': identity.get('cognitoIdentityId', None) or identity.get('user', None),
+ 'username': identity.get('user', None),
+ 'ip_address': identity.get('sourceIp', None),
+ 'cognito_identity_pool_id': identity.get('cognitoIdentityPoolId', None),
+ 'cognito_authentication_type': identity.get('cognitoAuthenticationType', None),
+ 'user_agent': identity.get('userAgent')
+ }
+ return {'user': user}
+
+ @staticmethod
+ def _get_http_interface(event):
+ if event.get('path') and event.get('httpMethod'):
+ request = {
+ "url": event.get('path'),
+ "method": event.get('httpMethod'),
+ "query_string": event.get('queryStringParameters', None),
+ "headers": event.get('headers', None) or [],
+ }
+ return {'request': request}
+
+ @staticmethod
+ def _get_extra_data(event, context):
+ extra_context = {
+ 'event': event,
+ 'aws_request_id': context.aws_request_id,
+ 'context': vars(context),
+ }
+
+ if context.client_context:
+ extra_context['client_context'] = {
+ 'client.installation_id': context.client_context.client.installation_id,
+ 'client.app_title': context.client_context.client.app_title,
+ 'client.app_version_name': context.client_context.client.app_version_name,
+ 'client.app_version_code': context.client_context.client.app_version_code,
+ 'client.app_package_name': context.client_context.client.app_package_name,
+ 'custom': context.client_context.custom,
+ 'env': context.client_context.env,
+ }
+ return extra_context
+
+
+