11import sys
22import traceback
3- from datetime import datetime , timedelta
4- from os import environ
3+ from datetime import datetime
54
5+ import chalice
66from chalice import Chalice , ChaliceViewError , Response
7+ from chalice .app import EventSourceHandler as ChaliceEventSourceHandler
78from sentry_sdk ._types import MYPY
89from sentry_sdk .hub import Hub , _should_send_default_pii
910from sentry_sdk .integrations import Integration
1011from sentry_sdk .integrations ._wsgi_common import _filter_headers
11- from sentry_sdk .integrations .aws_lambda import AwsLambdaIntegration
12+ from sentry_sdk .integrations .aws_lambda import _get_cloudwatch_logs_url
1213from sentry_sdk .utils import (
1314 AnnotatedValue ,
1415 capture_internal_exceptions ,
2122 from sentry_sdk ._types import Event , EventProcessor , Hint
2223
2324
25+ class EventSourceHandler (ChaliceEventSourceHandler ):
26+
27+ def __call__ (self , event , context ):
28+ hub = Hub .current
29+ client = hub .client
30+
31+ with hub .push_scope () as scope :
32+ try :
33+ event_obj = self .event_class (event , context )
34+ return self .func (event_obj )
35+ except Exception :
36+ scope .add_event_processor (
37+ _make_request_event_processor (
38+ event , context
39+ )
40+ )
41+ exc_info = sys .exc_info ()
42+ event , hint = event_from_exception (
43+ exc_info ,
44+ client_options = client .options ,
45+ mechanism = {"type" : "chalice" , "handled" : False },
46+ )
47+ hub .capture_event (event , hint = hint )
48+ hub .flush ()
49+
50+
2451def _get_view_function_response (app , view_function , function_args ):
2552 hub = Hub .current
2653 client = hub .client
@@ -83,7 +110,7 @@ def setup_once():
83110 # for @app.route()
84111 Chalice ._get_view_function_response = _get_view_function_response
85112 # for everything else (like events)
86- AwsLambdaIntegration . setup_once ()
113+ chalice . app . EventSourceHandler = EventSourceHandler
87114
88115
89116def _make_request_event_processor (current_request , lambda_context ):
@@ -92,36 +119,32 @@ def _make_request_event_processor(current_request, lambda_context):
92119
93120 def event_processor (event , hint , start_time = start_time ):
94121 # type: (Event, Hint, datetime) -> Optional[Event]
122+
95123 extra = event .setdefault ("extra" , {})
96- extra ["lambda" ] = {
124+
125+ extra ["Chalice-lambda" ] = {
97126 "function_name" : lambda_context .function_name ,
98127 "function_version" : lambda_context .function_version ,
99- "invoked_function_arn " : lambda_context .invoked_function_arn ,
128+ "Lambda ARN " : lambda_context .invoked_function_arn ,
100129 "aws_request_id" : lambda_context .aws_request_id ,
101130 }
102131
103- extra ["cloudwatch logs " ] = {
132+ extra ["cloudwatch-info " ] = {
104133 "url" : _get_cloudwatch_logs_url (lambda_context , start_time ),
105134 "log_group" : lambda_context .log_group_name ,
106135 "log_stream" : lambda_context .log_stream_name ,
107136 }
108137
109- request = event .get ("request" , {})
110- if "httpMethod" in current_request .context :
111- request ["method" ] = current_request .context ["httpMethod" ]
138+ request_info = event .get ("request" , {})
112139
113- request [ "url " ] = _get_url ( current_request , lambda_context )
140+ request_info [ "method " ] = current_request . context [ "httpMethod" ]
114141
115- if "query_params" in current_request .to_dict ():
116- request ["query_string" ] = current_request .query_params
142+ request_info ["query_string" ] = current_request .query_params
117143
118- if "headers" in current_request .to_dict ():
119- request ["headers" ] = _filter_headers (current_request .headers )
144+ request_info ["headers" ] = _filter_headers (current_request .headers )
120145
121146 if current_request ._body is None :
122- # Unfortunately couldn't find a way to get structured body from AWS
123- # event. Meaning every body is unstructured to us.
124- request ["data" ] = AnnotatedValue (
147+ request_info ["data" ] = AnnotatedValue (
125148 "" , {"rem" : [["!raw" , "x" , 0 , 0 ]]}
126149 )
127150
@@ -136,47 +159,8 @@ def event_processor(event, hint, start_time=start_time):
136159 if ip is not None :
137160 user_info .setdefault ("ip_address" , ip )
138161
139- event ["request" ] = request
162+ event ["request" ] = request_info
140163
141164 return event
142165
143166 return event_processor
144-
145-
146- def _get_url (event , context ):
147- # type: (Any, Any) -> str
148- path = event .context ["path" ]
149- headers = event .headers
150- host = headers .get ("Host" , None )
151- proto = headers .get ("X-Forwarded-Proto" , None )
152- if proto and host and path :
153- return "{}://{}{}" .format (proto , host , path )
154- return "awslambda:///{}" .format (context .function_name )
155-
156-
157- def _get_cloudwatch_logs_url (context , start_time ):
158- # type: (Any, datetime) -> str
159- """
160- Generates a CloudWatchLogs console URL based on the context object
161- Arguments:
162- context {Any} -- context from lambda handler
163- Returns:
164- str -- AWS Console URL to logs.
165- """
166- formatstring = "%Y-%m-%dT%H:%M:%S"
167-
168- url = (
169- "https://console.aws.amazon.com/cloudwatch/home?region={region}"
170- "#logEventViewer:group={log_group};stream={log_stream}"
171- ";start={start_time};end={end_time}"
172- ).format (
173- region = environ .get ("AWS_REGION" ),
174- log_group = context .log_group_name ,
175- log_stream = context .log_stream_name ,
176- start_time = (start_time - timedelta (seconds = 1 )).strftime (formatstring ),
177- end_time = (datetime .now () + timedelta (seconds = 2 )).strftime (
178- formatstring
179- ),
180- )
181-
182- return url
0 commit comments