def _request_wrapper(func, instance, args, kwargs): """ This is the wrapper of the requests. it parses the http's message to conclude the url, headers, and body. Finally, it add an event to the span, and run the wrapped function (http.client.HTTPConnection.send). """ data = safe_get_list(args, 0) with lumigo_safe_execute("parse requested streams"): if isinstance(data, BytesIO): current_pos = data.tell() data = data.read(MAX_READ_SIZE) args[0].seek(current_pos) host, method, headers, body, uri = ( getattr(instance, "host", None), getattr(instance, "_method", None), None, None, None, ) with lumigo_safe_execute("parse request"): if isinstance(data, bytes) and _BODY_HEADER_SPLITTER in data: headers, body = data.split(_BODY_HEADER_SPLITTER, 1) if _FLAGS_HEADER_SPLITTER in headers: request_info, headers = headers.split(_FLAGS_HEADER_SPLITTER, 1) headers = http.client.parse_headers(BytesIO(headers)) path_and_query_params = ( # Parse path from request info, remove method (GET | POST) and http version (HTTP/1.1) request_info.decode("ascii") .replace(method, "") .replace(instance._http_vsn_str, "") .strip() ) uri = f"{host}{path_and_query_params}" host = host or headers.get("Host") with lumigo_safe_execute("add request event"): if headers: SpansContainer.get_span().add_request_event( HttpRequest(host=host, method=method, uri=uri, headers=headers, body=body) ) else: SpansContainer.get_span().add_unparsed_request( HttpRequest(host=host, method=method, uri=uri, body=data) ) ret_val = func(*args, **kwargs) with lumigo_safe_execute("add response event"): SpansContainer.get_span().update_event_end_time() return ret_val
def _read_stream_wrapper_generator(stream_generator, instance): for partial_response in stream_generator: with lumigo_safe_execute("parse response.read_chunked"): SpansContainer.get_span().update_event_response( None, instance.status, instance.headers, partial_response ) yield partial_response
def _add_wrap_flag_to_context(*args): """ This function is here in order to validate that we didn't already wrap this invocation (using the sls plugin / auto instrumentation / etc.). We are adding lumigo's flag to the context, and check it's value in _is_context_already_wrapped. """ if len(args) >= 2: with lumigo_safe_execute("wrap context"): setattr(args[1], CONTEXT_WRAPPED_BY_LUMIGO_KEY, True)
def _read_wrapper(func, instance, args, kwargs): """ This is the wrapper of the function that can be called only after `getresponse` was called. """ ret_val = func(*args, **kwargs) if ret_val: with lumigo_safe_execute("parse response.read"): SpansContainer.get_span().update_event_response( None, instance.code, instance.headers, ret_val ) return ret_val
def _response_wrapper(func, instance, args, kwargs): """ This is the wrapper of the function that can be called only after that the http request was sent. Note that we don't examine the response data because it may change the original behaviour (ret_val.peek()). """ ret_val = func(*args, **kwargs) with lumigo_safe_execute("parse response"): headers = ret_val.headers status_code = ret_val.code SpansContainer.get_span().update_event_response(instance.host, status_code, headers, b"") return ret_val
def wrap_http_calls(): global already_wrapped if not already_wrapped: with lumigo_safe_execute("wrap http calls"): get_logger().debug("wrapping the http request") wrap_function_wrapper("http.client", "HTTPConnection.send", _request_wrapper) wrap_function_wrapper("botocore.awsrequest", "AWSRequest.__init__", _putheader_wrapper) wrap_function_wrapper("http.client", "HTTPConnection.getresponse", _response_wrapper) wrap_function_wrapper("http.client", "HTTPResponse.read", _read_wrapper) if importlib.util.find_spec("urllib3"): wrap_function_wrapper( "urllib3.response", "HTTPResponse.read_chunked", _read_stream_wrapper ) already_wrapped = True
def lambda_wrapper(*args, **kwargs): if str(os.environ.get(_KILL_SWITCH, "")).lower() == "true": return func(*args, **kwargs) if _is_context_already_wrapped(*args): return func(*args, **kwargs) _add_wrap_flag_to_context(*args) executed = False ret_val = None local_print = print local_logging_format = logging.Formatter.format try: if Configuration.enhanced_print: _enhance_output(args, local_print, local_logging_format) SpansContainer.create_span(*args, force=True) SpansContainer.get_span().start(*args) wrap_http_calls() try: executed = True ret_val = func(*args, **kwargs) except Exception as e: with lumigo_safe_execute("Customer's exception"): SpansContainer.get_span().add_exception_event(e, inspect.trace()) raise finally: SpansContainer.get_span().end(ret_val) if Configuration.enhanced_print: builtins.print = local_print logging.Formatter.format = local_logging_format return ret_val except Exception: # The case where our wrapping raised an exception if not executed: TimeoutMechanism.stop() get_logger().exception("exception in the wrapper", exc_info=True) return func(*args, **kwargs) else: raise
def parse_triggered_by(event: dict): """ This function parses the event and build the dictionary that describes the given event. The current possible values are: * {triggeredBy: unknown} * {triggeredBy: apigw, api: <host>, resource: <>, httpMethod: <>, stage: <>, identity: <>, referer: <>} """ with lumigo_safe_execute("triggered by"): if not isinstance(event, dict): return None if _is_supported_http_method(event): return parse_http_method(event) elif _is_supported_sns(event): return _parse_sns(event) elif _is_supported_streams(event): return _parse_streams(event) elif _is_supported_cw(event): return _parse_cw(event) elif _is_step_function(event): return _parse_step_function(event) return _parse_unknown(event)