Exemplo n.º 1
0
def test_prepare():
    trace = trace_factory.get_or_create_trace()
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter('always')
        trace.prepare()
        assert not trace.events
        assert trace.exceptions == []
        assert len(w) == 1
    trace.clear_events()
    trace.add_event(EventMock())
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter('always')
        trace.prepare()
        assert not trace.events
        assert trace.exceptions == []
        assert len(w) == 1

    trace.clear_events()
    trace.add_event(EventMock())
    with warnings.catch_warnings(record=True) as w:
        trace.prepare()
        trace.prepare()  # this call should NOT trigger a warning
        assert not trace.events
        assert trace.exceptions == []
        assert len(w) == 1
Exemplo n.º 2
0
def test_add_event():
    event = EventMock()
    trace = trace_factory.get_or_create_trace()
    trace.clear_events()
    for i in range(10):  # verify we can add more then 1 event
        trace.add_event(event)

        assert event is trace.events[i]
        assert event.terminated
Exemplo n.º 3
0
def test_send_with_sample_good_rate_with_error(wrapped_post, _):
    trace = trace_factory.get_or_create_trace()
    trace.runner = RunnerEventMock()
    trace.sample_rate = 0.6
    trace.runner.error_code = ErrorCode.ERROR
    trace.add_event(trace.runner)
    trace.token = 'a'
    trace.add_event(EventMock())
    trace_factory.send_traces()
    wrapped_post.assert_called_once()
Exemplo n.º 4
0
def test_send_on_error_only_off_no_error(wrapped_post):
    trace = trace_factory.get_or_create_trace()
    trace.token = 'a'
    trace.runner = RunnerEventMock()
    trace.runner.error_code = ErrorCode.OK
    event = EventMock()
    event.resource['metadata'] = datetime.fromtimestamp(1000)
    trace.add_event(event)
    trace_factory.send_traces()
    wrapped_post.assert_called_once()
Exemplo n.º 5
0
def test_send_on_error_only_with_error(wrapped_post):
    trace = trace_factory.get_or_create_trace()
    trace.send_trace_only_on_error = True
    trace.runner = mock.MagicMock()
    trace.runner.error_code = ErrorCode.ERROR
    trace.token = 'a'
    event = EventMock()
    event.resource['metadata'] = datetime.fromtimestamp(1000)
    trace.add_event(event)
    trace.send_traces()
    wrapped_post.assert_called_once()
Exemplo n.º 6
0
def test_send_with_split_on_small_trace(wrapped_post, monkeypatch):
    # Should be low enough to force trace split.
    monkeypatch.setenv('EPSAGON_MAX_TRACE_SIZE', '500')
    trace = trace_factory.get_or_create_trace()
    trace.runner = RunnerEventMock()
    trace.add_event(trace.runner)
    trace.token = 'a'
    trace.split_on_send = True
    event = EventMock()
    trace.add_event(event)
    trace_factory.send_traces()
    wrapped_post.assert_called_once()
Exemplo n.º 7
0
def test_send_with_split_on_small_trace(wrapped_post):
    # Should be low enough to force trace split.
    os.environ['EPSAGON_MAX_TRACE_SIZE'] = '500'
    trace = trace_factory.get_or_create_trace()
    trace.runner = mock.MagicMock()
    trace.add_event(trace.runner)
    trace.token = 'a'
    trace.split_on_send = True
    event = EventMock()
    trace.add_event(event)
    trace.send_traces()
    wrapped_post.assert_called_once()
    os.environ.pop('EPSAGON_MAX_TRACE_SIZE')
Exemplo n.º 8
0
def test_send_with_split_off(wrapped_post):
    # Should be low enough to force trace split.
    os.environ['EPSAGON_MAX_TRACE_SIZE'] = '500'
    trace = trace_factory.get_or_create_trace()
    trace.runner = RunnerEventMock()
    trace.add_event(trace.runner)
    trace.token = 'a'
    trace.split_on_send = False
    for _ in range(10):
        event = EventMock()
        trace.add_event(event)
    trace_factory.send_traces()
    wrapped_post.assert_called_once()
Exemplo n.º 9
0
def test_event_with_datetime(wrapped_post):
    trace = trace_factory.get_or_create_trace()

    trace.token = 'a'
    event = EventMock()
    event.resource['metadata'] = datetime.fromtimestamp(1000)
    trace.add_event(event)
    trace_factory.send_traces()
    wrapped_post.assert_called_with(
        '',
        data=json.dumps(trace.to_dict(), cls=TraceEncoder),
        timeout=epsagon.constants.SEND_TIMEOUT,
        headers={'Authorization': 'Bearer {}'.format(trace.token)})
Exemplo n.º 10
0
def test_event_with_datetime(wrapped_post):
    epsagon.utils.init(token='token', app_name='app-name', collector_url='collector')
    trace = trace_factory.get_or_create_trace()

    event = EventMock()
    event.resource['metadata'] = datetime.fromtimestamp(1000)
    trace.add_event(event)
    trace_factory.send_traces()
    wrapped_post.assert_called_with(
        'POST',
        'collector',
        body=json.dumps(trace.to_dict(), cls=TraceEncoder),
        timeout=epsagon.constants.SEND_TIMEOUT,
    )
Exemplo n.º 11
0
    def _gcp_wrapper(*args, **kwargs):
        """
        Generic google function wrapper
        """
        trace = epsagon.trace.trace_factory.get_or_create_trace()
        trace.prepare()

        try:
            runner = GoogleFunctionRunner(time.time(), )
        # pylint: disable=W0703
        except Exception as exception:
            # Regress to python runner.
            warnings.warn(
                'GCP environment 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

        result = None
        try:
            result = func(*args, **kwargs)
            return result
        # pylint: disable=W0703
        except Exception as exception:
            runner.set_exception(exception, traceback.format_exc())
            raise
        finally:
            try:
                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:
                trace.add_event(runner)
                epsagon.trace.trace_factory.send_traces()
            # pylint: disable=W0703
            except Exception:
                pass
Exemplo n.º 12
0
def test_to_dict():
    trace = epsagon.trace.Trace()
    expected_dict = {
        'token': 'token',
        'app_name': 'app_name',
        'events': [EventMockWithCounter(i) for i in range(10)],
        'exceptions': 'exceptions',
        'version': 'version',
        'platform': 'platform'
    }

    trace.token = expected_dict['token']
    trace.app_name = expected_dict['app_name']
    for event in [EventMockWithCounter(i) for i in range(10)]:
        trace.add_event(event)
    trace.exceptions = expected_dict['exceptions']
    trace.version = expected_dict['version']
    trace.platform = expected_dict['platform']
    trace_dict = trace.to_dict()
    assert trace_dict == trace.to_dict()
Exemplo n.º 13
0
    def _before_request(self):
        """
        Runs when new request comes in.
        :return: None.
        """
        # Ignoring non relevant content types.
        self.ignored_request = ignore_request('', request.path.lower())

        if self.ignored_request:
            return

        trace = epsagon.trace.trace_factory.get_or_create_trace()
        trace.prepare()

        # Create flask runner with current request.
        try:
            self.runner = epsagon.runners.flask.FlaskRunner(
                time.time(), self.app, request)
            trace.set_runner(self.runner)

            # Collect metadata in case this is a container.
            collect_container_metadata(self.runner.resource['metadata'])

        # pylint: disable=W0703
        except Exception as exception:
            # Regress to python runner.
            warnings.warn('Could not extract request', EpsagonWarning)
            trace.add_exception(exception, traceback.format_exc())

        # Extract HTTP trigger data.
        try:
            trigger = epsagon.triggers.http.HTTPTriggerFactory.factory(
                time.time(), request)
            if trigger:
                trace.add_event(trigger)
        # pylint: disable=W0703
        except Exception as exception:
            trace.add_exception(
                exception,
                traceback.format_exc(),
            )
Exemplo n.º 14
0
def test_send_big_trace(wrapped_post):
    trace = trace_factory.get_or_create_trace()
    runner = RunnerEventMock()

    trace.set_runner(runner)

    for _ in range(2):
        trace.add_event(BigEventMock())
    trace_factory.send_traces()

    assert len(trace.to_dict()['events']) == 3
    for event in trace.to_dict()['events']:
        if event['origin'] == 'runner':
            assert event['resource']['metadata']['is_trimmed']

    wrapped_post.assert_called_with(
        'POST',
        'collector',
        body=json.dumps(trace.to_dict()),
        timeout=epsagon.constants.SEND_TIMEOUT,
    )
Exemplo n.º 15
0
def test_send_big_trace(wrapped_post):
    trace = trace_factory.get_or_create_trace()
    runner = RunnerEventMock()

    trace.set_runner(runner)
    trace.token = 'a'

    for _ in range(2):
        trace.add_event(BigEventMock())
    trace_factory.send_traces()

    assert len(trace.to_dict()['events']) == 3
    for event in trace.to_dict()['events']:
        if event['origin'] == 'runner':
            assert event['resource']['metadata']['is_trimmed']

    wrapped_post.assert_called_with(
        '',
        data=json.dumps(trace.to_dict()),
        timeout=epsagon.constants.SEND_TIMEOUT,
        headers={'Authorization': 'Bearer {}'.format(trace.token)})
Exemplo n.º 16
0
    def _tencent_function_wrapper(*args, **kwargs):
        """
        Generic SCF function wrapper
        """
        start_time = time.time()
        cold_start_duration = start_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.tencent_function.TencentFunctionRunner(
                start_time, context)
            trace.set_runner(runner)
        # pylint: disable=W0703
        except Exception as exception:
            # Regress to python runner.
            warnings.warn(
                'SCF 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'][
                'tencent.scf.cold_start_duration'] = cold_start_duration
        constants.COLD_START = False

        try:
            trace.add_event(
                TencentFunctionTriggerFactory.factory(start_time, event,
                                                      context, runner))
        # pylint: disable=W0703
        except Exception as exception:
            trace.add_exception(exception,
                                traceback.format_exc(),
                                additional_data={'event': event})

        result = None
        try:
            result = func(*args, **kwargs)
            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']['tencent.scf.return_data'] = (
                        result)
            # pylint: disable=W0703
            except Exception as exception:
                trace.add_exception(
                    exception,
                    traceback.format_exc(),
                )

            try:
                epsagon.trace.trace_factory.send_traces()
            # pylint: disable=W0703
            except Exception:
                epsagon.utils.print_debug('Failed to send SCF trace')
Exemplo n.º 17
0
    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
Exemplo n.º 18
0
    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
Exemplo n.º 19
0
    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
Exemplo n.º 20
0
    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')