Beispiel #1
0
 def __init__(self,
              *,
              plugin_executor=None,
              api_key=None,
              disable_trace=False,
              disable_metric=True,
              disable_log=True,
              opts=None,
              **ignored):
     super(TestWrapper, self).__init__(api_key, disable_trace,
                                       disable_metric, disable_log, opts)
     ExecutionContextManager.set_provider(
         TracingExecutionContextProvider())  #TODO
     self.application_info_provider = GlobalApplicationInfoProvider()
     self._set_application_info("Foresight", "TestSuite", "TestSuite")
     self.plugin_context = PluginContext(
         application_info=self.application_info_provider.
         get_application_info(),
         request_count=0,
         executor=plugin_executor,
         api_key=self.api_key)
     max_test_log_count = ConfigProvider.get(
         config_names.THUNDRA_TEST_LOG_COUNT_MAX)
     self.config.log_config = LogConfig(
         sampler=MaxCountAwareSampler(max_test_log_count))
     self.plugins = wrapper_utils.initialize_plugins(self.plugin_context,
                                                     disable_trace,
                                                     disable_metric,
                                                     disable_log,
                                                     config=self.config)
     TestWrapper.__instance = self
Beispiel #2
0
    def __init__(self, api_key=None, disable_trace=False, disable_metric=True, disable_log=True, opts=None):
        super(FastapiWrapper, self).__init__(api_key, disable_trace, disable_metric, disable_log, opts)
        self.application_info_provider = GlobalApplicationInfoProvider()
        ExecutionContextManager.set_provider(TracingExecutionContextProvider())
        self.plugin_context = PluginContext(application_info=self.application_info_provider.get_application_info(),
                                            request_count=0,
                                            executor=fastapi_executor,
                                            api_key=self.api_key)

        self.plugins = wrapper_utils.initialize_plugins(self.plugin_context, disable_trace, disable_metric, disable_log,
                                                        config=self.config)

        web_wrapper_utils.update_application_info(self.application_info_provider, self.plugin_context.application_info,
                                                  constants.ClassNames['FASTAPI'])

        FastapiWrapper.__instance = self
Beispiel #3
0
    def __init__(self, api_key=None, disable_trace=False, disable_metric=True, disable_log=True, opts=None):
        try:
            from django.conf import settings
            django_settings = getattr(settings, 'THUNDRA', None)
            if django_settings is not None:
                configure({'config': django_settings})
        except:
            pass

        super(DjangoWrapper, self).__init__(api_key, disable_trace, disable_metric, disable_log, opts)
        self.application_info_provider = GlobalApplicationInfoProvider()
        ExecutionContextManager.set_provider(TracingExecutionContextProvider())
        self.plugin_context = PluginContext(application_info=self.application_info_provider.get_application_info(),
                                            request_count=0,
                                            executor=django_executor,
                                            api_key=self.api_key)

        self.plugins = wrapper_utils.initialize_plugins(self.plugin_context, disable_trace, disable_metric, disable_log,
                                                        config=self.config)

        web_wrapper_utils.update_application_info(self.application_info_provider, self.plugin_context.application_info,
                                                  constants.ClassNames['DJANGO'])
Beispiel #4
0
    def __init__(self,
                 api_key=None,
                 disable_trace=False,
                 disable_metric=True,
                 disable_log=True,
                 opts=None):
        super(LambdaWrapper, self).__init__(api_key, disable_trace,
                                            disable_metric, disable_log, opts)
        self.application_info_provider = GlobalApplicationInfoProvider(
            LambdaApplicationInfoProvider())
        self.plugin_context = PluginContext(
            application_info=self.application_info_provider.
            get_application_info(),
            request_count=0,
            executor=lambda_executor,
            api_key=self.api_key)

        ExecutionContextManager.set_provider(GlobalExecutionContextProvider())
        self.plugins = wrapper_utils.initialize_plugins(
            self.plugin_context, disable_trace, disable_metric, disable_log,
            self.config)

        self.timeout_margin = ConfigProvider.get(
            config_names.THUNDRA_LAMBDA_TIMEOUT_MARGIN,
            constants.DEFAULT_LAMBDA_TIMEOUT_MARGIN)

        if not ConfigProvider.get(
                config_names.THUNDRA_TRACE_INSTRUMENT_DISABLE):
            # Pass thundra instance to integration for wrapping handler wrappers
            handler_wrappers.patch_modules(self)

        self.ptvsd_imported = False
        if ConfigProvider.get(
                config_names.THUNDRA_LAMBDA_DEBUGGER_ENABLE,
                ConfigProvider.get(
                    config_names.THUNDRA_LAMBDA_DEBUGGER_AUTH_TOKEN)):
            self.initialize_debugger()
Beispiel #5
0
class DjangoWrapper(BaseWrapper):

    def __init__(self, api_key=None, disable_trace=False, disable_metric=True, disable_log=True, opts=None):
        try:
            from django.conf import settings
            django_settings = getattr(settings, 'THUNDRA', None)
            if django_settings is not None:
                configure({'config': django_settings})
        except:
            pass

        super(DjangoWrapper, self).__init__(api_key, disable_trace, disable_metric, disable_log, opts)
        self.application_info_provider = GlobalApplicationInfoProvider()
        ExecutionContextManager.set_provider(TracingExecutionContextProvider())
        self.plugin_context = PluginContext(application_info=self.application_info_provider.get_application_info(),
                                            request_count=0,
                                            executor=django_executor,
                                            api_key=self.api_key)

        self.plugins = wrapper_utils.initialize_plugins(self.plugin_context, disable_trace, disable_metric, disable_log,
                                                        config=self.config)

        web_wrapper_utils.update_application_info(self.application_info_provider, self.plugin_context.application_info,
                                                  constants.ClassNames['DJANGO'])

    def before_request(self, request):
        # Execution context initialization
        execution_context = wrapper_utils.create_execution_context()
        execution_context.platform_data['request'] = request

        # Execute plugin hooks before running user's handler
        self.plugin_context.request_count += 1
        self.execute_hook('before:invocation', execution_context)

        return execution_context

    def after_request(self, response):
        execution_context = ExecutionContextManager.get()
        if response:
            execution_context.response = response
        self.prepare_and_send_reports_async(execution_context)

    def process_exception(self, exception):
        execution_context = ExecutionContextManager.get()
        execution_context.error = exception

    def __call__(self, original_func):
        if hasattr(original_func, "_thundra_wrapped") or ConfigProvider.get(config_names.THUNDRA_DISABLE, False):
            return original_func

        @wraps(original_func)
        def wrapper(request, *args, **kwargs):
            if getattr(request, '_thundra_wrapped', False):
                return original_func(request, *args, **kwargs)
            setattr(request, '_thundra_wrapped', True)
            try:
                execution_context = self.before_request(request)
            except Exception as e:
                logger.error("Error during the before part of Thundra: {}".format(e))
                return original_func(request, *args, **kwargs)

            response = None
            # Invoke user handler
            try:
                response = original_func(request, *args, **kwargs)
            except Exception as e:
                try:
                    execution_context.error = {
                        'type': type(e).__name__,
                        'message': str(e),
                        'traceback': traceback.format_exc()
                    }
                    self.after_request(response)
                except Exception as e_in:
                    logger.error("Error during the after part of Thundra: {}".format(e_in))
                raise e

            try:
                self.after_request(response)
            except Exception as e:
                logger.error("Error during the after part of Thundra: {}".format(e))
            return response

        setattr(wrapper, '_thundra_wrapped', True)
        return wrapper

    call = __call__
Beispiel #6
0
class TestWrapper(BaseWrapper):
    """
        Foresight wrapper util class. It keeps all the generic information for execution of test. 
        Creating test suite and test case, change application info, start and finish trace and invocation plugins.
    """

    __instance = None

    @staticmethod
    def get_instance(*args, **kwargs):
        return TestWrapper.__instance if TestWrapper.__instance else TestWrapper(
            *args, **kwargs)

    def __init__(self,
                 *,
                 plugin_executor=None,
                 api_key=None,
                 disable_trace=False,
                 disable_metric=True,
                 disable_log=True,
                 opts=None,
                 **ignored):
        super(TestWrapper, self).__init__(api_key, disable_trace,
                                          disable_metric, disable_log, opts)
        ExecutionContextManager.set_provider(
            TracingExecutionContextProvider())  #TODO
        self.application_info_provider = GlobalApplicationInfoProvider()
        self._set_application_info("Foresight", "TestSuite", "TestSuite")
        self.plugin_context = PluginContext(
            application_info=self.application_info_provider.
            get_application_info(),
            request_count=0,
            executor=plugin_executor,
            api_key=self.api_key)
        max_test_log_count = ConfigProvider.get(
            config_names.THUNDRA_TEST_LOG_COUNT_MAX)
        self.config.log_config = LogConfig(
            sampler=MaxCountAwareSampler(max_test_log_count))
        self.plugins = wrapper_utils.initialize_plugins(self.plugin_context,
                                                        disable_trace,
                                                        disable_metric,
                                                        disable_log,
                                                        config=self.config)
        TestWrapper.__instance = self

    def _set_application_info(self, application_class_name,
                              application_domain_name, application_name):
        try:
            import pytest
            self.application_info_provider.update({
                "applicationClassName":
                application_class_name,
                "applicationDomainName":
                application_domain_name,
                "applicationName":
                application_name,
                "applicationVersion":
                pytest.__version__
            })
            self.application_info_provider.update(
                {"applicationId": self._get_default_application_id()})
        except Exception as err:
            logger.error(
                "Test wrapper utils application info set error: {}".format(
                    err))
            pass

    def _get_default_application_id(self):
        try:
            application_info = self.application_info_provider.get_application_info(
            )
            application_id = "python:test:pytest:{}:{}".format(
                application_info.get("applicationClassName"),
                application_info.get("applicationName"))
            return application_id.lower()
        except Exception as err:
            logger.error("get default application id error: {}".format(err))
            pass
        return "python:test:pytest:{}".format(utils.create_uuid4())

    def change_app_info(self, application_info):
        """It shall be used for when changing flow form test suite to test case or vice-versa.

        Args:
            application_info (ApplicationInfo): Thundra application info
        """
        try:
            self.application_info_provider.update(application_info.to_json())
        except Exception as err:
            logger.error(
                "Test wrapper application info change error: {}".format(err))
            pass

    def create_test_suite_execution_context(self, test_suite_name=None):
        """ Creating test suite execution context

        Args:
            test_suite_name (str, optional): Unique test suite name. Defaults to None.

        Returns:
            TestSuiteExecutionContext: [description]
        """
        try:
            transaction_id = utils.create_uuid4()
            start_timestamp = utils.current_milli_time()
            return TestSuiteExecutionContext(transaction_id=transaction_id,
                                             start_timestamp=start_timestamp,
                                             node_id=test_suite_name)
        except Exception as err:
            logger.error(
                "create test suite execution context error: {}".format(err))
            pass

    def create_test_case_execution_context(self,
                                           name,
                                           test_suite_name,
                                           test_case_id,
                                           app_info,
                                           parent_transaction_id=None):
        """Creating test case execution context

        Args:
            name (str): Test case name
            test_suite_name (str): unique test suite name
            test_case_id (str): unique test case id
            app_info (ApplicationInfo): Thundra application info
            parent_transaction_id (str, optional): Parent span transaction id for Thundra apm. Defaults to None.

        Returns:
            TestCaseExecutionContext: Store execution context for test case.
        """
        try:
            transaction_id = utils.create_uuid4()
            start_timestamp = utils.current_milli_time()
            test_class = app_info.application_class_name if app_info.application_class_name else None
            return TestCaseExecutionContext(
                transaction_id=transaction_id,
                start_timestamp=start_timestamp,
                test_suite_name=test_suite_name,
                node_id=test_case_id,
                parent_transaction_id=parent_transaction_id,
                name=name,
                method=ForesightConstants.TEST_OPERATION_NAME,
                test_class=test_class)
        except Exception as err:
            logger.error(
                "create test case execution context error: {}".format(err))
            pass

    def start_trace(self, execution_context, tracer):
        """Calling after thundra trace pluging has been called by execute_hook. Create trace span.

        Args:
            execution_context (TestSuiteExecutionContext | TestSuiteExecutionContext): Execution context
            tracer (ThundraTracer): Thundra Tracer
        """
        try:
            operation_name = execution_context.get_operation_name()
            trace_id = utils.create_uuid4()
            scope = tracer.start_active_span(
                operation_name=operation_name,
                start_time=execution_context.start_timestamp,
                trace_id=trace_id,
                execution_context=execution_context,
                transaction_id=execution_context.transaction_id,
                finish_on_close=False,
            )
            root_span = scope.span
            app_info = self.application_info_provider.get_application_info()
            root_span.class_name = app_info.get("applicationClassName")
            root_span.domain_name = app_info.get("applicationDomainName")
            root_span.service_name = app_info.get("applicationName")
            execution_context.span_id = root_span.context.span_id
            execution_context.root_span = root_span
            execution_context.scope = scope
            execution_context.trace_id = trace_id

            root_span = execution_context.root_span
        except Exception as err:
            logger.error("Test wrapper start trace error: {}".format(err))
            pass

    def finish_trace(self, execution_context):
        """Finish trace.

        Args:
            execution_context (TestSuiteExecutionContext | TestSuiteExecutionContext): Execution context
        """
        try:
            root_span = execution_context.root_span
            scope = execution_context.scope
            try:
                root_span.finish(f_time=execution_context.finish_timestamp)
            except Exception:
                # TODO: handle root span finish errors
                pass
            finally:
                scope.close()
        except Exception as err:
            logger.error("test wrapper finish trace error: {}".format(err))
            pass

    def start_invocation(self, execution_context):
        """Calling after thundra invocation pluging has been called by execute_hook. Create trace span.

        Args:
            execution_context (TestSuiteExecutionContext | TestSuiteExecutionContext): Execution context
        """
        try:
            execution_context.invocation_data = wrapper_utils.create_invocation_data(
                self.plugin_context, execution_context)
        except Exception as err:
            logger.error("test wrapper start invocation error: {}".format(err))
            pass

    def finish_invocation(self, execution_context):
        """Finish invocation.

        Args:
            execution_context (TestSuiteExecutionContext | TestSuiteExecutionContext): Execution context
        """
        try:
            wrapper_utils.finish_invocation(execution_context)
        except Exception as err:
            logger.error("finish invocation error: {}".format(err))
            pass

    def before_test_process(self, execution_context):
        """Invoke trace and invocation plugings before each test suite and test case.

        Args:
            execution_context (TestSuiteExecutionContext | TestCaseExecutionContext): Thundra test execution context
        """
        try:
            self.execute_hook("before:invocation", execution_context)
        except Exception as err:
            logger.error("test wrapper before test process: {}".format(err))
            pass

    def after_test_process(self, execution_context):
        """Send collected data about test suit or test case after each finish.

        Args:
            execution_context (TestSuiteExecutionContext | TestCaseExecutionContext): Thundra test execution context
        """
        try:
            self.prepare_and_send_reports_async(execution_context)
        except Exception as err:
            logger.error(
                "test wrapper after test process error: {}".format(err))
            pass

    def send_test_run_data(self, test_run_event):
        """Send test run data. test_run_monitoring data has been updated by application info because
         reporter has been used tih info whilst preparing composite data. 

        Args:
            test_run_event (TestRunStart, TestRunStatus, TestRunFinish): Tests event data
        """
        try:
            test_run_monitoring_data = test_run_event.get_monitoring_data()
            self.reporter.send_reports([test_run_monitoring_data],
                                       test_run_event=True)
        except Exception as err:
            logger.error(
                "test wrapper send test run data error: {}".format(err))
            pass
Beispiel #7
0
class FastapiWrapper(BaseWrapper):    
    __instance = None
    
    @staticmethod
    def get_instance():
        return FastapiWrapper() if FastapiWrapper.__instance is None else FastapiWrapper.__instance

    def __init__(self, api_key=None, disable_trace=False, disable_metric=True, disable_log=True, opts=None):
        super(FastapiWrapper, self).__init__(api_key, disable_trace, disable_metric, disable_log, opts)
        self.application_info_provider = GlobalApplicationInfoProvider()
        ExecutionContextManager.set_provider(TracingExecutionContextProvider())
        self.plugin_context = PluginContext(application_info=self.application_info_provider.get_application_info(),
                                            request_count=0,
                                            executor=fastapi_executor,
                                            api_key=self.api_key)

        self.plugins = wrapper_utils.initialize_plugins(self.plugin_context, disable_trace, disable_metric, disable_log,
                                                        config=self.config)

        web_wrapper_utils.update_application_info(self.application_info_provider, self.plugin_context.application_info,
                                                  constants.ClassNames['FASTAPI'])

        FastapiWrapper.__instance = self

    def before_request(self, request, req_body):
        execution_context = wrapper_utils.create_execution_context()
        execution_context.platform_data["request"] = request
        execution_context.platform_data["request"]["body"] = req_body

        self.plugin_context.request_count += 1
        self.execute_hook("before:invocation", execution_context)

        return execution_context


    def after_request(self, execution_context):
        self.prepare_and_send_reports_async(execution_context)

    def error_handler(self, error, execution_context):
        if error:
            execution_context.error = error

        self.prepare_and_send_reports_async(execution_context)
        
        

    def __call__(self, original_func):

        import inspect
        if hasattr(original_func, "_thundra_wrapped") or ConfigProvider.get(config_names.THUNDRA_DISABLE, False):
            return original_func

        @wraps(original_func)
        async def wrapper(*args, **kwargs):
            request = kwargs.get("request")
            if request is None or request.scope.get('_thundra_wrapped', False):
                if inspect.iscoroutinefunction(original_func):
                    return await original_func(*args, **kwargs)
                else:
                    return original_func(*args, **kwargs)

            request.scope['_thundra_wrapped'] = True
            try:
                req_body = request._body if hasattr(request, "_body") else None
                request.scope["thundra_execution_context"] = self.before_request(request.scope, req_body)
                execution_context = request.scope["thundra_execution_context"]
            except Exception as e:
                logger.error('Error during the before part of Thundra: {}'.format(e))
                if inspect.iscoroutinefunction(original_func):
                    return await original_func(*args, **kwargs)
                else:
                    return original_func(*args, **kwargs)

            response = None
            try:
                if inspect.iscoroutinefunction(original_func):
                    response = await original_func(*args, **kwargs)
                else:
                    response = original_func(*args, **kwargs)
            except Exception as e:
                try:
                    error = {
                        'type': type(e).__name__,
                        'message': str(e),
                        'traceback': traceback.format_exc()
                    }
                    self.error_handler(error, execution_context)
                except Exception as e_in:
                    logger.error("Error during the after part of Thundra: {}".format(e_in))
                raise e

            try:
                execution_context.response = response
                self.after_request(execution_context)
            except Exception as e:
                logger.error("Error during the after part of Thundra: {}".format(e))
            return response

        setattr(wrapper, '_thundra_wrapped', True)
        return wrapper
Beispiel #8
0
class LambdaWrapper(BaseWrapper):
    def __init__(self,
                 api_key=None,
                 disable_trace=False,
                 disable_metric=True,
                 disable_log=True,
                 opts=None):
        super(LambdaWrapper, self).__init__(api_key, disable_trace,
                                            disable_metric, disable_log, opts)
        self.application_info_provider = GlobalApplicationInfoProvider(
            LambdaApplicationInfoProvider())
        self.plugin_context = PluginContext(
            application_info=self.application_info_provider.
            get_application_info(),
            request_count=0,
            executor=lambda_executor,
            api_key=self.api_key)

        ExecutionContextManager.set_provider(GlobalExecutionContextProvider())
        self.plugins = wrapper_utils.initialize_plugins(
            self.plugin_context, disable_trace, disable_metric, disable_log,
            self.config)

        self.timeout_margin = ConfigProvider.get(
            config_names.THUNDRA_LAMBDA_TIMEOUT_MARGIN,
            constants.DEFAULT_LAMBDA_TIMEOUT_MARGIN)

        if not ConfigProvider.get(
                config_names.THUNDRA_TRACE_INSTRUMENT_DISABLE):
            # Pass thundra instance to integration for wrapping handler wrappers
            handler_wrappers.patch_modules(self)

        self.ptvsd_imported = False
        if ConfigProvider.get(
                config_names.THUNDRA_LAMBDA_DEBUGGER_ENABLE,
                ConfigProvider.get(
                    config_names.THUNDRA_LAMBDA_DEBUGGER_AUTH_TOKEN)):
            self.initialize_debugger()

    def __call__(self, original_func):
        if hasattr(original_func, "_thundra_wrapped") or ConfigProvider.get(
                config_names.THUNDRA_DISABLE, False):
            return original_func

        @wraps(original_func)
        def wrapper(event, context):
            application_name = self.plugin_context.application_info.get(
                'applicationName')
            self.application_info_provider.update({
                'applicationId':
                LambdaApplicationInfoProvider.get_application_id(
                    context, application_name=application_name)
            })

            # Execution context initialization
            execution_context = wrapper_utils.create_execution_context()
            try:
                execution_context.platform_data[
                    'originalEvent'] = copy.deepcopy(event)
            except:
                execution_context.platform_data['originalEvent'] = event
            execution_context.platform_data['originalContext'] = context
            ExecutionContextManager.set(execution_context)

            # Before running user's handler
            try:
                if ConfigProvider.get(
                        config_names.THUNDRA_LAMBDA_WARMUP_WARMUPAWARE,
                        False) and self.check_and_handle_warmup_request(event):
                    return None

                self.plugin_context.request_count += 1
                self.execute_hook('before:invocation', execution_context)

                timeout_duration = self.get_timeout_duration(context)
            except Exception as e:
                logger.error(
                    "Error during the before part of Thundra: {}".format(e))
                return original_func(event, context)

            # Invoke user handler
            try:
                response = None
                with Timeout(timeout_duration, self.timeout_handler,
                             execution_context):
                    if ConfigProvider.get(
                            config_names.THUNDRA_LAMBDA_DEBUGGER_ENABLE,
                            ConfigProvider.get(
                                config_names.THUNDRA_LAMBDA_DEBUGGER_AUTH_TOKEN
                            )) and self.ptvsd_imported:
                        self.start_debugger_tracing(context)

                    response = original_func(event, context)
                    execution_context.response = response
            except Exception as e:
                try:
                    execution_context.error = {
                        'type': type(e).__name__,
                        'message': str(e),
                        'traceback': traceback.format_exc()
                    }
                    self.prepare_and_send_reports(execution_context)
                except Exception as e_in:
                    logger.error(
                        "Error during the after part of Thundra: {}".format(
                            e_in))
                    pass
                raise e
            finally:
                if ConfigProvider.get(
                        config_names.THUNDRA_LAMBDA_DEBUGGER_ENABLE,
                        ConfigProvider.get(
                            config_names.THUNDRA_LAMBDA_DEBUGGER_AUTH_TOKEN)
                ) and self.ptvsd_imported:
                    self.stop_debugger_tracing()

            # After having run the user's handler
            try:
                self.prepare_and_send_reports(execution_context)
            except Exception as e:
                logger.error(
                    "Error during the after part of Thundra: {}".format(e))

            ExecutionContextManager.clear()
            return response

        setattr(wrapper, '_thundra_wrapped', True)
        return wrapper

    call = __call__

    def initialize_debugger(self):
        if PY2:
            logger.error(
                "Online debugging not supported in python2.7. Supported versions: 3.6, 3.7, 3.8"
            )
            return
        try:
            import ptvsd
            self.ptvsd_imported = True
        except Exception as e:
            logger.error(
                "Could not import ptvsd. Thundra ptvsd layer must be added")

    def start_debugger_tracing(self, context):
        try:
            import ptvsd
            ptvsd.tracing(True)

            ptvsd.enable_attach(address=(
                "localhost",
                ConfigProvider.get(config_names.THUNDRA_LAMBDA_DEBUGGER_PORT)))
            if not self.debugger_process:
                env = os.environ.copy()
                env['BROKER_HOST'] = str(
                    ConfigProvider.get(
                        config_names.THUNDRA_LAMBDA_DEBUGGER_BROKER_HOST))
                env['BROKER_PORT'] = str(
                    ConfigProvider.get(
                        config_names.THUNDRA_LAMBDA_DEBUGGER_BROKER_PORT))
                env['DEBUGGER_PORT'] = str(
                    ConfigProvider.get(
                        config_names.THUNDRA_LAMBDA_DEBUGGER_PORT))
                env['AUTH_TOKEN'] = str(
                    ConfigProvider.get(
                        config_names.THUNDRA_LAMBDA_DEBUGGER_AUTH_TOKEN))
                env['SESSION_NAME'] = str(
                    ConfigProvider.get(
                        config_names.THUNDRA_LAMBDA_DEBUGGER_SESSION_NAME))

                if hasattr(context, 'get_remaining_time_in_millis'):
                    env['SESSION_TIMEOUT'] = str(
                        context.get_remaining_time_in_millis() +
                        int(time.time() * 1000.0))

                debug_bridge_file_path = os.path.join(
                    os.path.dirname(__file__), '../../debug/bridge.py')
                self.debugger_process = subprocess.Popen(
                    ["python", debug_bridge_file_path],
                    stdout=subprocess.PIPE,
                    stdin=subprocess.PIPE,
                    env=env)

            start_time = time.time()
            debug_process_running = True
            while time.time() < (start_time + ConfigProvider.get(config_names.THUNDRA_LAMBDA_DEBUGGER_WAIT_MAX) / 1000) \
                    and not ptvsd.is_attached():
                if self.debugger_process.poll() is None:
                    ptvsd.wait_for_attach(0.01)
                else:
                    debug_process_running = False
                    break

            if not ptvsd.is_attached():
                if debug_process_running:
                    logger.error('Couldn\'t complete debugger handshake in {} milliseconds.' \
                                 .format(ConfigProvider.get(config_names.THUNDRA_LAMBDA_DEBUGGER_WAIT_MAX)))
                ptvsd.tracing(False)
            else:
                ptvsd.tracing(True)

        except Exception as e:
            logger.error(
                "error while setting tracing true to debugger using ptvsd: {}".
                format(e))

    def stop_debugger_tracing(self):
        try:
            import ptvsd
            ptvsd.tracing(False)
            from ptvsd.attach_server import debugger_attached
            debugger_attached.clear()
        except Exception as e:
            logger.error(
                "error while setting tracing false to debugger using ptvsd: {}"
                .format(e))

        try:
            if self.debugger_process:
                o, e = self.debugger_process.communicate(b"fin\n")
                debug_logger("Thundra debugger process output: {}".format(
                    o.decode("utf-8")))
                self.debugger_process = None
        except Exception as e:
            self.debugger_process = None
            logger.error(
                "error while killing proxy process for debug: {}".format(e))

    def check_and_handle_warmup_request(self, event):

        # Check whether it is empty request which is used as default warmup request
        if not event:
            print("Received warmup request as empty message. " +
                  "Handling with 90 milliseconds delay ...")
            time.sleep(0.1)
            return True
        else:
            if isinstance(event, str):
                # Check whether it is warmup request
                if event.startswith('#warmup'):
                    delayTime = 90
                    args = event[len('#warmup'):].strip().split()
                    # Warmup messages are in '#warmup wait=<waitTime>' format
                    # Iterate over all warmup arguments
                    for arg in args:
                        argParts = arg.split('=')
                        # Check whether argument is in key=value format
                        if len(argParts) == 2:
                            argName = argParts[0]
                            argValue = argParts[1]
                            # Check whether argument is "wait" argument
                            # which specifies extra wait time before returning from request
                            if argName == 'wait':
                                waitTime = int(argValue)
                                delayTime += waitTime
                    print("Received warmup request as warmup message. " +
                          "Handling with " + str(delayTime) +
                          " milliseconds delay ...")
                    time.sleep(delayTime / 1000)
                    return True
            return False

    def get_timeout_duration(self, context):
        timeout_duration = 0
        if hasattr(context, 'get_remaining_time_in_millis'):
            timeout_duration = context.get_remaining_time_in_millis(
            ) - self.timeout_margin
            if timeout_duration <= 0:
                timeout_duration = context.get_remaining_time_in_millis() - \
                                   constants.DEFAULT_LAMBDA_TIMEOUT_MARGIN
                logger.warning(
                    'Given timeout margin is bigger than lambda timeout duration and '
                    'since the difference is negative, it is set to default value ('
                    + str(constants.DEFAULT_LAMBDA_TIMEOUT_MARGIN) + ')')

        return timeout_duration / 1000.0

    def timeout_handler(self, execution_context):
        execution_context.timeout = True
        execution_context.error = TimeoutError('Task timed out')
        self.prepare_and_send_reports(execution_context)