def test_timeout_handler_called(wrapped_post): """ Sanity """ context = ContextMock(300) runner = RunnerEventMock() trace = trace_factory.get_or_create_trace() trace.token = 'a' trace.set_timeout_handler(context) trace.set_runner(runner) time.sleep(0.5) trace.reset_timeout_handler() assert trace.trace_sent assert wrapped_post.called
def test_timeout_handler_called(wrapped_post): """ Sanity """ context = ContextMock(DEFAULT_SEND_TIMEOUT_MS * 1.1) runner = RunnerEventMock() trace = trace_factory.get_or_create_trace() trace.token = 'a' trace.set_timeout_handler(context) trace.set_runner(runner) time.sleep((DEFAULT_SEND_TIMEOUT_MS / 1000) * 1.5) trace.reset_timeout_handler() assert trace.trace_sent assert wrapped_post.called
def test_timeout_send_not_called_twice(wrapped_post): """ In case of a timeout send trace, validate no trace is sent afterwards (if the flow continues) """ context = ContextMock(300) runner = RunnerEventMock() trace = trace_factory.get_or_create_trace() trace.token = 'a' trace.set_timeout_handler(context) trace.set_runner(runner) time.sleep(0.5) trace.reset_timeout_handler() assert trace.trace_sent assert wrapped_post.call_count == 1
def test_timeout_happyflow_handler_call(wrapped_post): """ Test in case we already sent the traces on happy flow, that timeout handler call won't send them again. """ context = ContextMock(300) runner = RunnerEventMock() trace = trace_factory.get_or_create_trace() trace.set_runner(runner) trace.token = 'a' trace_factory.send_traces() trace.set_timeout_handler(context) time.sleep(0.5) trace.reset_timeout_handler() assert trace.trace_sent assert wrapped_post.call_count == 1
def _lambda_wrapper(*args, **kwargs): """ Generic Lambda function wrapper """ trace = epsagon.trace.trace_factory.get_or_create_trace() trace.prepare() try: event, context = args except ValueError: # This can happen when someone manually calls handler without # parameters / sends kwargs. In such case we ignore this trace. return func(*args, **kwargs) try: runner = epsagon.runners.aws_lambda.LambdaRunner( time.time(), context) trace.set_runner(runner) # pylint: disable=W0703 except Exception as exception: # Regress to python runner. warnings.warn( 'Lambda context is invalid, using simple python wrapper', EpsagonWarning) trace.add_exception(exception, traceback.format_exc()) return epsagon.wrappers.python_function.wrap_python_function( func, args, kwargs) constants.COLD_START = False try: trace.add_event( epsagon.triggers.aws_lambda.LambdaTriggerFactory.factory( time.time(), event, context)) # pylint: disable=W0703 except Exception as exception: trace.add_exception(exception, traceback.format_exc(), additional_data={'event': event}) if not trace.disable_timeout_send: trace.set_timeout_handler(context) result = None try: result = func(*args, **kwargs) if trace.propagate_lambda_id and isinstance(result, dict): result[EPSAGON_EVENT_ID_KEY] = runner.event_id runner.resource['metadata']['propagation_enabled'] = True return result # pylint: disable=W0703 except Exception as exception: runner.set_exception(exception, traceback.format_exc(), handled=False) raise finally: try: _add_status_code(runner, result) if not trace.metadata_only: runner.resource['metadata']['return_value'] = result # pylint: disable=W0703 except Exception as exception: trace.add_exception( exception, traceback.format_exc(), ) try: if not trace.disable_timeout_send: epsagon.trace.Trace.reset_timeout_handler() # pylint: disable=W0703 except Exception: pass try: epsagon.trace.trace_factory.send_traces() # pylint: disable=W0703 except Exception: pass
def _lambda_wrapper(*args, **kwargs): """ Generic Step Function wrapper """ trace = epsagon.trace.trace_factory.get_or_create_trace() trace.prepare() try: event, context = args except ValueError: # This can happen when someone manually calls handler without # parameters / sends kwargs. In such case we ignore this trace. return func(*args, **kwargs) try: runner = epsagon.runners.aws_lambda.StepLambdaRunner( time.time(), context) trace.set_runner(runner) # pylint: disable=W0703 except Exception as exception: # Regress to python runner. warnings.warn( 'Lambda context is invalid, using simple python wrapper', EpsagonWarning) trace.add_exception(exception, traceback.format_exc()) return epsagon.wrappers.python_function.wrap_python_function( func, args, kwargs) constants.COLD_START = False try: trace.add_event( epsagon.triggers.aws_lambda.LambdaTriggerFactory.factory( time.time(), event, context)) # pylint: disable=W0703 except Exception as exception: trace.add_exception(exception, traceback.format_exc(), additional_data={'event': event}) trace.set_timeout_handler(context) result = None try: result = func(*args, **kwargs) steps_dict = epsagon.utils.find_in_object(event, STEP_DICT_NAME) if isinstance(result, dict): # If the step functions data is not present, then this is the # First step. if steps_dict is None: steps_dict = {'id': str(uuid4()), 'step_num': 0} # Otherwise, just advance the steps number by one. else: # don't change trigger data steps_dict = copy.deepcopy(steps_dict) steps_dict['step_num'] += 1 result[STEP_DICT_NAME] = steps_dict runner.add_step_data(steps_dict) return result # pylint: disable=W0703 except Exception as exception: runner.set_exception(exception, traceback.format_exc(), handled=False) raise finally: try: _add_status_code(runner, result) if not trace.metadata_only: runner.resource['metadata']['return_value'] = ( copy.deepcopy(result)) # pylint: disable=W0703 except Exception as exception: trace.add_exception( exception, traceback.format_exc(), ) try: epsagon.trace.Trace.reset_timeout_handler() # pylint: disable=W0703 except Exception: pass try: epsagon.trace.trace_factory.send_traces() # pylint: disable=W0703 except Exception: pass
def _lambda_wrapper(*args, **kwargs): """ Generic Step Function wrapper """ cold_start_duration = time.time() - constants.COLD_START_TIME trace = epsagon.trace.trace_factory.get_or_create_trace() trace.prepare() try: event, context = args except ValueError: # This can happen when someone manually calls handler without # parameters / sends kwargs. In such case we ignore this trace. return func(*args, **kwargs) try: runner = epsagon.runners.aws_lambda.StepLambdaRunner( time.time(), context ) trace.set_runner(runner) # pylint: disable=W0703 except Exception as exception: # Regress to python runner. warnings.warn( 'Lambda context is invalid, using simple python wrapper', EpsagonWarning ) trace.add_exception( exception, traceback.format_exc() ) return epsagon.wrappers.python_function.wrap_python_function( func, args, kwargs ) if constants.COLD_START: runner.resource['metadata'][ 'aws.lambda.cold_start_duration' ] = cold_start_duration constants.COLD_START = False try: trace.add_event( epsagon.triggers.aws_lambda.LambdaTriggerFactory.factory( time.time(), event, context ) ) # pylint: disable=W0703 except Exception as exception: trace.add_exception( exception, traceback.format_exc(), additional_data={'event': event} ) trace.set_timeout_handler(context) result = None try: result = func(*args, **kwargs) steps_data = epsagon.utils.find_in_object( event, STEP_DICT_NAME ) if isinstance(result, dict): epsagon.utils.print_debug( 'Step function result type is dict, steps_data={}'.format( steps_data ) ) # If the step functions data is not present, then this is the # First step. if steps_data is None: epsagon.utils.print_debug( 'Could not find existing steps data' ) steps_dict = {'id': str(uuid4()), 'step_num': 0} path = [] # Otherwise, just advance the steps number by one. else: # don't change trigger data steps_dict, path = steps_data steps_dict = copy.deepcopy(steps_dict) if 'step_num' in steps_dict: steps_dict['step_num'] += 1 epsagon.utils.print_debug( 'Steps data found, new dict={}'.format(steps_dict) ) else: steps_dict = {'id': str(uuid4()), 'step_num': 0} epsagon.utils.print_debug( 'Steps data not found, new dict={}'.format( steps_dict ) ) result_path = result # Tries to inject the steps data in the configured # or same path where it was found if isinstance(trace.step_dict_output_path, list): path = trace.step_dict_output_path try: for sub_path in path: result_path = result_path.get(sub_path) except Exception as exception: # pylint: disable=broad-except epsagon.utils.print_debug( 'Could not put steps in path={}'.format(path) ) if result_path: epsagon.utils.print_debug( 'Adding steps dict to result_path={}'.format( result_path ) ) result_path[STEP_DICT_NAME] = steps_dict else: epsagon.utils.print_debug( 'Adding steps dict to root result' ) result[STEP_DICT_NAME] = steps_dict runner.add_step_data(steps_dict) return result # pylint: disable=W0703 except Exception as exception: runner.set_exception( exception, traceback.format_exc(), handled=False ) raise finally: try: _add_status_code(runner, result) if not trace.metadata_only: runner.resource['metadata']['return_value'] = ( copy.deepcopy(result) ) # pylint: disable=W0703 except Exception as exception: trace.add_exception( exception, traceback.format_exc(), ) try: epsagon.trace.Trace.reset_timeout_handler() # pylint: disable=W0703 except Exception: pass try: epsagon.trace.trace_factory.send_traces() # pylint: disable=W0703 except Exception: pass
def _lambda_wrapper(*args, **kwargs): """ Generic Lambda function wrapper """ cold_start_duration = time.time() - constants.COLD_START_TIME trace = epsagon.trace.trace_factory.get_or_create_trace() trace.prepare() try: event, context = args except ValueError: # This can happen when someone manually calls handler without # parameters / sends kwargs. In such case we ignore this trace. return func(*args, **kwargs) if isinstance(event, dict): ignored_payloads = _get_ignored_payloads() if ignored_payloads and event in ignored_payloads: return func(*args, **kwargs) if os.environ.get( 'AWS_LAMBDA_INITIALIZATION_TYPE' ) == 'provisioned-concurrency': constants.COLD_START = False try: runner = epsagon.runners.aws_lambda.LambdaRunner( time.time(), context ) trace.set_runner(runner) # pylint: disable=W0703 except Exception as exception: # Regress to python runner. warnings.warn( 'Lambda context is invalid, using simple python wrapper', EpsagonWarning ) trace.add_exception( exception, traceback.format_exc() ) return epsagon.wrappers.python_function.wrap_python_function( func, args, kwargs ) if constants.COLD_START: runner.resource['metadata'][ 'aws.lambda.cold_start_duration' ] = cold_start_duration constants.COLD_START = False try: trace.add_event( epsagon.triggers.aws_lambda.LambdaTriggerFactory.factory( time.time(), event, context ) ) # pylint: disable=W0703 except Exception as exception: trace.add_exception( exception, traceback.format_exc(), additional_data={'event': event} ) if not trace.disable_timeout_send: trace.set_timeout_handler(context) result = None try: result = func(*args, **kwargs) if trace.propagate_lambda_id and isinstance(result, dict): result[EPSAGON_EVENT_ID_KEY] = runner.event_id runner.resource['metadata']['propagation_enabled'] = True return result # pylint: disable=W0703 except Exception as exception: runner.set_exception( exception, traceback.format_exc(), handled=False ) raise finally: try: _add_status_code(runner, result) if not trace.metadata_only: runner.resource['metadata']['return_value'] = result # pylint: disable=W0703 except Exception as exception: trace.add_exception( exception, traceback.format_exc(), ) try: if not trace.disable_timeout_send: epsagon.trace.Trace.reset_timeout_handler() # pylint: disable=W0703 except Exception: pass try: epsagon.trace.trace_factory.send_traces() # pylint: disable=W0703 except Exception: epsagon.utils.print_debug('Failed to send Lambda trace')