Beispiel #1
0
    def __init__(self, config=None, **defaults):
        # configure loggers first
        cls = self.__class__
        self.logger = logging.getLogger('%s.%s' % (cls.__module__, cls.__name__))
        self.error_logger = logging.getLogger('elasticapm.errors')
        self.state = ClientState()

        self.instrumentation_store = None
        self.processors = []
        self.filter_exception_types_dict = {}
        self._send_timer = None
        self._transports = {}
        self._service_info = None

        self.config = Config(config, default_dict=defaults)
        if self.config.errors:
            for msg in self.config.errors.values():
                self.error_logger.error(msg)
            self.config.disable_send = True
            return

        self._transport_class = import_string(self.config.transport_class)

        for exc_to_filter in (self.config.filter_exception_types or []):
            exc_to_filter_type = exc_to_filter.split(".")[-1]
            exc_to_filter_module = ".".join(exc_to_filter.split(".")[:-1])
            self.filter_exception_types_dict[exc_to_filter_type] = exc_to_filter_module

        self.processors = [import_string(p) for p in self.config.processors] if self.config.processors else []

        self.instrumentation_store = TransactionsStore(
            lambda: self._get_stack_info_for_trace(
                stacks.iter_stack_frames(),
                library_frame_context_lines=self.config.source_lines_span_library_frames,
                in_app_frame_context_lines=self.config.source_lines_span_app_frames,
                with_locals=self.config.collect_local_variables in ('all', 'transactions'),
                locals_processor_func=lambda local_var: varmap(lambda k, v: shorten(
                    v,
                    list_length=self.config.local_var_list_max_length,
                    string_length=self.config.local_var_max_length,
                ), local_var)
            ),
            collect_frequency=self.config.flush_interval,
            sample_rate=self.config.transaction_sample_rate,
            max_spans=self.config.transaction_max_spans,
            max_queue_size=self.config.max_queue_size,
            ignore_patterns=self.config.transactions_ignore_patterns,
        )
        self.include_paths_re = stacks.get_path_regex(self.config.include_paths) if self.config.include_paths else None
        self.exclude_paths_re = stacks.get_path_regex(self.config.exclude_paths) if self.config.exclude_paths else None
        compat.atexit_register(self.close)
Beispiel #2
0
def get_instrumentation_objects():
    for cls_str in _cls_register:
        if cls_str not in _instrumentation_singletons:
            cls = import_string(cls_str)
            _instrumentation_singletons[cls_str] = cls()

        obj = _instrumentation_singletons[cls_str]
        yield obj
    def load_processors(self):
        """
        Loads processors from self.config.processors, as well as constants.HARDCODED_PROCESSORS.
        Duplicate processors (based on the path) will be discarded.

        :return: a list of callables
        """
        processors = itertools.chain(self.config.processors, constants.HARDCODED_PROCESSORS)
        seen = {}
        # setdefault has the nice property that it returns the value that it just set on the dict
        return [seen.setdefault(path, import_string(path)) for path in processors if path not in seen]
Beispiel #4
0
 def _get_transport(self, parsed_url):
     if self.async_mode and is_master_process():
         # when in the master process, always use SYNC mode. This avoids
         # the danger of being forked into an inconsistent threading state
         self.logger.info(
             'Sending message synchronously while in master '
             'process. PID: %s', os.getpid())
         return import_string(defaults.SYNC_TRANSPORT_CLASS)(parsed_url)
     if parsed_url not in self._transports:
         self._transports[parsed_url] = self._transport_class(parsed_url)
     return self._transports[parsed_url]
Beispiel #5
0
 def register(self, class_path):
     """
     Register a new metric set
     :param class_path: a string with the import path of the metricset class
     """
     if class_path in self._metricsets:
         return
     else:
         try:
             class_obj = import_string(class_path)
             self._metricsets[class_path] = class_obj(self)
         except ImportError as e:
             logger.warning("Could not register %s metricset: %s", class_path, compat.text_type(e))
Beispiel #6
0
def get_client():
    """
    Get an ElasticAPM client.

    :param client:
    :return:
    :rtype: elasticapm.base.Client
    """
    if _get_client():
        return _get_client()

    config = getattr(django_settings, "ELASTIC_APM", {})
    client = config.get("CLIENT", default_client_class)
    client_class = import_string(client)
    instance = client_class()
    # `instance` will already be in elasticapm.base.CLIENT_SINGLETON due to the
    # `__init__()` for Client
    return instance
Beispiel #7
0
def get_client(client=None):
    """
    Get an ElasticAPM client.

    :param client:
    :return:
    :rtype: elasticapm.base.Client
    """
    global _client

    tmp_client = client is not None
    if not tmp_client:
        config = getattr(django_settings, "ELASTIC_APM", {})
        client = config.get("CLIENT", default_client_class)

    if _client[0] != client:
        client_class = import_string(client)
        instance = client_class()
        if not tmp_client:
            _client = (client, instance)
        return instance
    return _client[1]
Beispiel #8
0
    def __init__(self, config=None, **inline):
        # configure loggers first
        cls = self.__class__
        self.logger = logging.getLogger("%s.%s" %
                                        (cls.__module__, cls.__name__))
        self.error_logger = logging.getLogger("elasticapm.errors")

        self.tracer = None
        self.processors = []
        self.filter_exception_types_dict = {}
        self._service_info = None

        self.config = Config(config, inline_dict=inline)
        if self.config.errors:
            for msg in self.config.errors.values():
                self.error_logger.error(msg)
            self.config.disable_send = True

        headers = {
            "Content-Type": "application/x-ndjson",
            "Content-Encoding": "gzip",
            "User-Agent": "elasticapm-python/%s" % elasticapm.VERSION,
        }

        if self.config.secret_token:
            headers["Authorization"] = "Bearer %s" % self.config.secret_token
        transport_kwargs = {
            "metadata": self._build_metadata(),
            "headers": headers,
            "verify_server_cert": self.config.verify_server_cert,
            "timeout": self.config.server_timeout,
            "max_flush_time": self.config.api_request_time / 1000.0,
            "max_buffer_size": self.config.api_request_size,
        }
        self._transport = import_string(self.config.transport_class)(
            compat.urlparse.urljoin(self.config.server_url,
                                    constants.EVENTS_API_PATH),
            **transport_kwargs)

        for exc_to_filter in self.config.filter_exception_types or []:
            exc_to_filter_type = exc_to_filter.split(".")[-1]
            exc_to_filter_module = ".".join(exc_to_filter.split(".")[:-1])
            self.filter_exception_types_dict[
                exc_to_filter_type] = exc_to_filter_module

        self.processors = [import_string(p) for p in self.config.processors
                           ] if self.config.processors else []

        if platform.python_implementation() == "PyPy":
            # PyPy introduces a `_functools.partial.__call__` frame due to our use
            # of `partial` in AbstractInstrumentedModule
            skip_modules = ("elasticapm.", "_functools")
        else:
            skip_modules = ("elasticapm.", )

        self.tracer = Tracer(
            frames_collector_func=lambda: list(
                stacks.iter_stack_frames(start_frame=inspect.currentframe(),
                                         skip_top_modules=skip_modules)),
            frames_processing_func=lambda frames: self.
            _get_stack_info_for_trace(
                frames,
                library_frame_context_lines=self.config.
                source_lines_span_library_frames,
                in_app_frame_context_lines=self.config.
                source_lines_span_app_frames,
                with_locals=self.config.collect_local_variables in
                ("all", "transactions"),
                locals_processor_func=lambda local_var: varmap(
                    lambda k, v: shorten(
                        v,
                        list_length=self.config.local_var_list_max_length,
                        string_length=self.config.local_var_max_length,
                    ),
                    local_var,
                ),
            ),
            queue_func=self.queue,
            sample_rate=self.config.transaction_sample_rate,
            max_spans=self.config.transaction_max_spans,
            span_frames_min_duration=self.config.span_frames_min_duration,
            ignore_patterns=self.config.transactions_ignore_patterns,
        )
        self.include_paths_re = stacks.get_path_regex(
            self.config.include_paths) if self.config.include_paths else None
        self.exclude_paths_re = stacks.get_path_regex(
            self.config.exclude_paths) if self.config.exclude_paths else None
        compat.atexit_register(self.close)
Beispiel #9
0
 def get_handler(self, name):
     return import_string(name)
    def __init__(self, config=None, **inline):
        # configure loggers first
        cls = self.__class__
        self.logger = get_logger("%s.%s" % (cls.__module__, cls.__name__))
        self.error_logger = get_logger("elasticapm.errors")

        self.tracer = None
        self.processors = []
        self.filter_exception_types_dict = {}
        self._service_info = None

        config = Config(config, inline_dict=inline)
        if config.errors:
            for msg in config.errors.values():
                self.error_logger.error(msg)
            config.disable_send = True
        self.config = VersionedConfig(config, version=None)

        # Insert the log_record_factory into the logging library
        # The LogRecordFactory functionality is only available on python 3.2+
        if compat.PY3 and not self.config.disable_log_record_factory:
            record_factory = logging.getLogRecordFactory()
            # Only way to know if it's wrapped is to create a log record
            throwaway_record = record_factory(__name__, logging.DEBUG, __file__, 252, "dummy_msg", [], None)
            if not hasattr(throwaway_record, "elasticapm_labels"):
                self.logger.debug("Inserting elasticapm log_record_factory into logging")

                # Late import due to circular imports
                import elasticapm.handlers.logging as elastic_logging

                new_factory = elastic_logging.log_record_factory(record_factory)
                logging.setLogRecordFactory(new_factory)

        headers = {
            "Content-Type": "application/x-ndjson",
            "Content-Encoding": "gzip",
            "User-Agent": "elasticapm-python/%s" % elasticapm.VERSION,
        }

        if self.config.secret_token:
            headers["Authorization"] = "Bearer %s" % self.config.secret_token
        transport_kwargs = {
            "metadata": self._build_metadata(),
            "headers": headers,
            "verify_server_cert": self.config.verify_server_cert,
            "server_cert": self.config.server_cert,
            "timeout": self.config.server_timeout,
            "max_flush_time": self.config.api_request_time / 1000.0,
            "max_buffer_size": self.config.api_request_size,
            "processors": self.load_processors(),
        }
        self._api_endpoint_url = compat.urlparse.urljoin(
            self.config.server_url if self.config.server_url.endswith("/") else self.config.server_url + "/",
            constants.EVENTS_API_PATH,
        )
        self._transport = import_string(self.config.transport_class)(self._api_endpoint_url, **transport_kwargs)

        for exc_to_filter in self.config.filter_exception_types or []:
            exc_to_filter_type = exc_to_filter.split(".")[-1]
            exc_to_filter_module = ".".join(exc_to_filter.split(".")[:-1])
            self.filter_exception_types_dict[exc_to_filter_type] = exc_to_filter_module

        if platform.python_implementation() == "PyPy":
            # PyPy introduces a `_functools.partial.__call__` frame due to our use
            # of `partial` in AbstractInstrumentedModule
            skip_modules = ("elasticapm.", "_functools")
        else:
            skip_modules = ("elasticapm.",)

        self.tracer = Tracer(
            frames_collector_func=lambda: list(
                stacks.iter_stack_frames(
                    start_frame=inspect.currentframe(), skip_top_modules=skip_modules, config=self.config
                )
            ),
            frames_processing_func=lambda frames: self._get_stack_info_for_trace(
                frames,
                library_frame_context_lines=self.config.source_lines_span_library_frames,
                in_app_frame_context_lines=self.config.source_lines_span_app_frames,
                with_locals=self.config.collect_local_variables in ("all", "transactions"),
                locals_processor_func=lambda local_var: varmap(
                    lambda k, v: shorten(
                        v,
                        list_length=self.config.local_var_list_max_length,
                        string_length=self.config.local_var_max_length,
                        dict_length=self.config.local_var_dict_max_length,
                    ),
                    local_var,
                ),
            ),
            queue_func=self.queue,
            config=self.config,
            agent=self,
        )
        self.include_paths_re = stacks.get_path_regex(self.config.include_paths) if self.config.include_paths else None
        self.exclude_paths_re = stacks.get_path_regex(self.config.exclude_paths) if self.config.exclude_paths else None
        self._metrics = MetricsRegistry(
            self.config.metrics_interval / 1000.0, self.queue, ignore_patterns=self.config.disable_metrics
        )
        for path in self.config.metrics_sets:
            self._metrics.register(path)
        if self.config.breakdown_metrics:
            self._metrics.register("elasticapm.metrics.sets.breakdown.BreakdownMetricSet")
        compat.atexit_register(self.close)
        if self.config.central_config:
            self._config_updater = IntervalTimer(
                update_config, 1, "eapm conf updater", daemon=True, args=(self,), evaluate_function_interval=True
            )
            self._config_updater.start()
        else:
            self._config_updater = None
Beispiel #11
0
    def __init__(self,
                 app_name=None,
                 secret_token=None,
                 transport_class=None,
                 include_paths=None,
                 exclude_paths=None,
                 timeout=None,
                 hostname=None,
                 auto_log_stacks=None,
                 string_max_length=None,
                 list_max_length=None,
                 processors=None,
                 filter_exception_types=None,
                 servers=None,
                 async_mode=None,
                 traces_send_freq_secs=None,
                 transactions_ignore_patterns=None,
                 git_ref=None,
                 app_version=None,
                 **kwargs):
        self.app_name = self.secret_token = self.git_ref = self.app_version = None
        # configure loggers first
        cls = self.__class__
        self.logger = logging.getLogger('%s.%s' %
                                        (cls.__module__, cls.__name__))
        self.error_logger = logging.getLogger('elasticapm.errors')
        self.state = ClientState()
        self._configure(app_name=app_name,
                        secret_token=secret_token,
                        git_ref=git_ref,
                        app_version=app_version)
        self.servers = servers or defaults.SERVERS
        self.async_mode = (async_mode is True or
                           (defaults.ASYNC_MODE and async_mode is not False))
        if not transport_class:
            transport_class = (defaults.ASYNC_TRANSPORT_CLASS
                               if self.async_mode else
                               defaults.SYNC_TRANSPORT_CLASS)
        self._transport_class = import_string(transport_class)
        self._transports = {}

        # servers may be set to a NoneType (for Django)
        if self.servers and not (self.app_name and self.secret_token):
            msg = 'Missing configuration for ElasticAPM client. Please see documentation.'
            self.logger.info(msg)

        self.is_send_disabled = (os.environ.get('ELASTIC_APM_DISABLE_SEND',
                                                '').lower() in ('1', 'true'))
        if self.is_send_disabled:
            self.logger.info(
                'Not sending any data to APM Server due to ELASTIC_APM_DISABLE_SEND '
                'environment variable')

        self.include_paths = set(include_paths or defaults.INCLUDE_PATHS)
        self.exclude_paths = set(exclude_paths or defaults.EXCLUDE_PATHS)
        self.timeout = int(timeout or defaults.TIMEOUT)
        self.hostname = six.text_type(hostname or defaults.HOSTNAME)
        self.auto_log_stacks = bool(auto_log_stacks
                                    or defaults.AUTO_LOG_STACKS)

        self.string_max_length = int(string_max_length
                                     or defaults.MAX_LENGTH_STRING)
        self.list_max_length = int(list_max_length or defaults.MAX_LENGTH_LIST)
        self.traces_send_freq_secs = (traces_send_freq_secs
                                      or defaults.TRACES_SEND_FREQ_SECS)

        self.filter_exception_types_dict = {}
        for exc_to_filter in (filter_exception_types or []):
            exc_to_filter_type = exc_to_filter.split(".")[-1]
            exc_to_filter_module = ".".join(exc_to_filter.split(".")[:-1])
            self.filter_exception_types_dict[
                exc_to_filter_type] = exc_to_filter_module

        if processors is None:
            self.processors = defaults.PROCESSORS
        else:
            self.processors = processors
        self.processors = [import_string(p) for p in self.processors]

        self.instrumentation_store = TransactionsStore(
            lambda: self.get_stack_info_for_trace(iter_stack_frames(), False),
            self.traces_send_freq_secs, transactions_ignore_patterns)
        atexit_register(self.close)
Beispiel #12
0
    def __init__(self, config=None, **inline):
        # configure loggers first
        cls = self.__class__
        self.logger = logging.getLogger("%s.%s" %
                                        (cls.__module__, cls.__name__))
        self.error_logger = logging.getLogger("elasticapm.errors")
        self.state = ClientState()

        self.transaction_store = None
        self.processors = []
        self.filter_exception_types_dict = {}
        self._send_timer = None
        self._transports = {}
        self._service_info = None

        self.config = Config(config, inline_dict=inline)
        if self.config.errors:
            for msg in self.config.errors.values():
                self.error_logger.error(msg)
            self.config.disable_send = True

        self._transport_class = import_string(self.config.transport_class)

        for exc_to_filter in self.config.filter_exception_types or []:
            exc_to_filter_type = exc_to_filter.split(".")[-1]
            exc_to_filter_module = ".".join(exc_to_filter.split(".")[:-1])
            self.filter_exception_types_dict[
                exc_to_filter_type] = exc_to_filter_module

        self.processors = [import_string(p) for p in self.config.processors
                           ] if self.config.processors else []

        if platform.python_implementation() == "PyPy":
            # PyPy introduces a `_functools.partial.__call__` frame due to our use
            # of `partial` in AbstractInstrumentedModule
            skip_modules = ("elasticapm.", "_functools")
        else:
            skip_modules = ("elasticapm.", )

        def frames_collector_func():
            return self._get_stack_info_for_trace(
                stacks.iter_stack_frames(skip_top_modules=skip_modules),
                library_frame_context_lines=self.config.
                source_lines_span_library_frames,
                in_app_frame_context_lines=self.config.
                source_lines_span_app_frames,
                with_locals=self.config.collect_local_variables
                in ("all", "transactions"),
                locals_processor_func=lambda local_var: varmap(
                    lambda k, v: shorten(
                        v,
                        list_length=self.config.local_var_list_max_length,
                        string_length=self.config.local_var_max_length,
                    ),
                    local_var,
                ),
            )

        self.transaction_store = TransactionsStore(
            frames_collector_func=frames_collector_func,
            collect_frequency=self.config.flush_interval,
            sample_rate=self.config.transaction_sample_rate,
            max_spans=self.config.transaction_max_spans,
            span_frames_min_duration=self.config.span_frames_min_duration_ms,
            max_queue_size=self.config.max_queue_size,
            ignore_patterns=self.config.transactions_ignore_patterns,
        )
        self.include_paths_re = stacks.get_path_regex(
            self.config.include_paths) if self.config.include_paths else None
        self.exclude_paths_re = stacks.get_path_regex(
            self.config.exclude_paths) if self.config.exclude_paths else None
        compat.atexit_register(self.close)
Beispiel #13
0
    def __init__(self, config=None, **inline):
        # configure loggers first
        cls = self.__class__
        self.logger = get_logger("%s.%s" % (cls.__module__, cls.__name__))
        self.error_logger = get_logger("elasticapm.errors")

        self._pid = None
        self._thread_starter_lock = threading.Lock()
        self._thread_managers = {}

        self.tracer = None
        self.processors = []
        self.filter_exception_types_dict = {}
        self._service_info = None

        self.check_python_version()

        config = Config(config, inline_dict=inline)
        if config.errors:
            for msg in config.errors.values():
                self.error_logger.error(msg)
            config.disable_send = True
        if config.service_name == "python_service":
            self.logger.warning(
                "No custom SERVICE_NAME was set -- using non-descript default 'python_service'"
            )
        self.config = VersionedConfig(config, version=None)

        # Insert the log_record_factory into the logging library
        # The LogRecordFactory functionality is only available on python 3.2+
        if compat.PY3 and not self.config.disable_log_record_factory:
            record_factory = logging.getLogRecordFactory()
            # Only way to know if it's wrapped is to create a log record
            throwaway_record = record_factory(__name__, logging.DEBUG,
                                              __file__, 252, "dummy_msg", [],
                                              None)
            if not hasattr(throwaway_record, "elasticapm_labels"):
                self.logger.debug(
                    "Inserting elasticapm log_record_factory into logging")

                # Late import due to circular imports
                import elasticapm.handlers.logging as elastic_logging

                new_factory = elastic_logging.log_record_factory(
                    record_factory)
                logging.setLogRecordFactory(new_factory)

        headers = {
            "Content-Type": "application/x-ndjson",
            "Content-Encoding": "gzip",
            "User-Agent": "elasticapm-python/%s" % elasticapm.VERSION,
        }

        transport_kwargs = {
            "headers": headers,
            "verify_server_cert": self.config.verify_server_cert,
            "server_cert": self.config.server_cert,
            "timeout": self.config.server_timeout,
            "processors": self.load_processors(),
        }
        self._api_endpoint_url = compat.urlparse.urljoin(
            self.config.server_url if self.config.server_url.endswith("/") else
            self.config.server_url + "/",
            constants.EVENTS_API_PATH,
        )
        transport_class = import_string(self.config.transport_class)
        self._transport = transport_class(self._api_endpoint_url, self,
                                          **transport_kwargs)
        self.config.transport = self._transport
        self._thread_managers["transport"] = self._transport

        for exc_to_filter in self.config.filter_exception_types or []:
            exc_to_filter_type = exc_to_filter.split(".")[-1]
            exc_to_filter_module = ".".join(exc_to_filter.split(".")[:-1])
            self.filter_exception_types_dict[
                exc_to_filter_type] = exc_to_filter_module

        if platform.python_implementation() == "PyPy":
            # PyPy introduces a `_functools.partial.__call__` frame due to our use
            # of `partial` in AbstractInstrumentedModule
            skip_modules = ("elasticapm.", "_functools")
        else:
            skip_modules = ("elasticapm.", )

        self.tracer = Tracer(
            frames_collector_func=lambda: list(
                stacks.iter_stack_frames(start_frame=inspect.currentframe(),
                                         skip_top_modules=skip_modules,
                                         config=self.config)),
            frames_processing_func=lambda frames: self.
            _get_stack_info_for_trace(
                frames,
                library_frame_context_lines=self.config.
                source_lines_span_library_frames,
                in_app_frame_context_lines=self.config.
                source_lines_span_app_frames,
                with_locals=self.config.collect_local_variables in
                ("all", "transactions"),
                locals_processor_func=lambda local_var: varmap(
                    lambda k, v: shorten(
                        v,
                        list_length=self.config.local_var_list_max_length,
                        string_length=self.config.local_var_max_length,
                        dict_length=self.config.local_var_dict_max_length,
                    ),
                    local_var,
                ),
            ),
            queue_func=self.queue,
            config=self.config,
            agent=self,
        )
        self.include_paths_re = stacks.get_path_regex(
            self.config.include_paths) if self.config.include_paths else None
        self.exclude_paths_re = stacks.get_path_regex(
            self.config.exclude_paths) if self.config.exclude_paths else None
        self._metrics = MetricsRegistry(self)
        for path in self.config.metrics_sets:
            self._metrics.register(path)
        if self.config.breakdown_metrics:
            self._metrics.register(
                "elasticapm.metrics.sets.breakdown.BreakdownMetricSet")
        if self.config.prometheus_metrics:
            self._metrics.register(
                "elasticapm.metrics.sets.prometheus.PrometheusMetrics")
        self._thread_managers["metrics"] = self._metrics
        compat.atexit_register(self.close)
        if self.config.central_config:
            self._thread_managers["config"] = self.config
        else:
            self._config_updater = None
        if self.config.use_elastic_excepthook:
            self.original_excepthook = sys.excepthook
            sys.excepthook = self._excepthook
        if config.enabled:
            self.start_threads()

        # Save this Client object as the global CLIENT_SINGLETON
        set_client(self)