def test_explicit_stack(logger): logger.info('This is a test of stacks', extra={'stack': iter_stack_frames()}) assert len(logger.client.events) == 1 event = logger.client.events.pop(0)['errors'][0] assert 'culprit' in event, event assert event['culprit'] == 'tests.handlers.logging.logging_tests.test_explicit_stack' assert 'message' in event['log'], event assert event['log']['message'] == 'This is a test of stacks' assert 'exception' not in event assert 'param_message' in event['log'] assert event['log']['param_message'] == 'This is a test of stacks' assert 'stacktrace' in event['log']
def test_explicit_stack(logger): logger.info("This is a test of stacks", extra={"stack": iter_stack_frames()}) assert len(logger.client.events) == 1 event = logger.client.events[ERROR][0] assert "culprit" in event, event assert event["culprit"] == "tests.handlers.logging.logging_tests.test_explicit_stack" assert "message" in event["log"], event assert event["log"]["message"] == "This is a test of stacks" assert "exception" not in event assert "param_message" in event["log"] assert event["log"]["param_message"] == "This is a test of stacks" assert "stacktrace" in event["log"]
def test_iter_stack_frames_skip_frames_by_module(): frames = [ Mock(f_lineno=1, f_globals={"__name__": "foo.bar"}), Mock(f_lineno=2, f_globals={"__name__": "foo.bar"}), Mock(f_lineno=3, f_globals={"__name__": "baz.bar"}), Mock(f_lineno=4, f_globals={"__name__": "foo.bar"}), ] iterated_frames = list(stacks.iter_stack_frames(frames, skip_top_modules=("foo.",))) assert len(iterated_frames) == 2 assert iterated_frames[0][1] == 3 assert iterated_frames[1][1] == 4
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) )
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)
def test_iter_stack_frames_max_frames(): iterated_frames = list( stacks.iter_stack_frames(get_me_more_test_frames(10), config=Mock(stack_trace_limit=5))) assert len(iterated_frames) == 5 assert iterated_frames[4][0].f_locals["count"] == 5 iterated_frames = list( stacks.iter_stack_frames(get_me_more_test_frames(10), config=Mock(stack_trace_limit=-1))) assert len(iterated_frames) == 10 iterated_frames = list( stacks.iter_stack_frames(get_me_more_test_frames(10), config=Mock(stack_trace_limit=0))) assert len(iterated_frames) == 0 iterated_frames = list( stacks.iter_stack_frames(get_me_more_test_frames(10), skip=3, config=Mock(stack_trace_limit=5))) assert len(iterated_frames) == 5 assert iterated_frames[4][0].f_locals["count"] == 8
def test_explicit_stack(self): self.logger.info('This is a test of stacks', extra={'stack': iter_stack_frames()}) self.assertEquals(len(self.client.events), 1) event = self.client.events.pop(0)['errors'][0] self.assertTrue('culprit' in event, event) self.assertEquals( event['culprit'], 'tests.handlers.logging.logging_tests.test_explicit_stack') self.assertTrue('message' in event['log'], event) self.assertEquals(event['log']['message'], 'This is a test of stacks') self.assertFalse('exception' in event) self.assertTrue('param_message' in event['log']) self.assertEquals(event['log']['param_message'], 'This is a test of stacks') self.assertTrue('stacktrace' in event['log'])
def test_iter_stack_frames_skip_frames(): frames = get_me_more_test_frames(4) iterated_frames = list(stacks.iter_stack_frames(frames, skip=3)) assert len(iterated_frames) == 1 assert iterated_frames[0][0].f_locals["count"] == 4
def _build_msg_for_logging(self, event_type, date=None, context=None, custom=None, stack=None, handled=True, **kwargs): """ Captures, processes and serializes an event into a dict object """ transaction = get_transaction() if transaction: transaction_context = deepcopy(transaction.context) else: transaction_context = {} event_data = {} if custom is None: custom = {} if not date: date = datetime.datetime.utcnow() if stack is None: stack = self.config.auto_log_stacks if context: transaction_context.update(context) context = transaction_context else: context = transaction_context event_data['context'] = context if transaction and transaction.tags: context['tags'] = deepcopy(transaction.tags) # if '.' not in event_type: # Assume it's a builtin event_type = 'elasticapm.events.%s' % event_type handler = self.get_handler(event_type) result = handler.capture(self, **kwargs) if self._filter_exception_type(result): return # data (explicit) culprit takes over auto event detection culprit = result.pop('culprit', None) if custom.get('culprit'): culprit = custom.pop('culprit') for k, v in compat.iteritems(result): if k not in event_data: event_data[k] = v log = event_data.get('log', {}) if stack and 'stacktrace' not in log: if stack is True: frames = stacks.iter_stack_frames(skip=3) else: frames = stack frames = stacks.get_stack_info( frames, with_locals=self.config.collect_local_variables in ('errors', 'all'), library_frame_context_lines=self.config. source_lines_error_library_frames, in_app_frame_context_lines=self.config. source_lines_error_app_frames, include_paths_re=self.include_paths_re, exclude_paths_re=self.exclude_paths_re, 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)) log['stacktrace'] = frames if 'stacktrace' in log and not culprit: culprit = stacks.get_culprit(log['stacktrace'], self.config.include_paths, self.config.exclude_paths) if 'level' in log and isinstance(log['level'], compat.integer_types): log['level'] = logging.getLevelName(log['level']).lower() if log: event_data['log'] = log if culprit: event_data['culprit'] = culprit if 'custom' in context: context['custom'].update(custom) else: context['custom'] = custom # Run the data through processors for processor in self.processors: event_data = processor(self, event_data) # Make sure all data is coerced event_data = transform(event_data) if 'exception' in event_data: event_data['exception']['handled'] = bool(handled) event_data.update({ 'timestamp': date.strftime(constants.TIMESTAMP_FORMAT), }) transaction = get_transaction() if transaction: event_data['transaction'] = {'id': transaction.id} return self._build_msg({'errors': [event_data]})
def build_msg_for_logging(self, event_type, data=None, date=None, extra=None, stack=None, **kwargs): """ Captures, processes and serializes an event into a dict object """ if data is None: data = {} if extra is None: extra = {} if not date: date = datetime.datetime.utcnow() if stack is None: stack = self.auto_log_stacks if 'context' not in data: data['context'] = context = {} else: context = data['context'] # if '.' not in event_type: # Assume it's a builtin event_type = 'elasticapm.events.%s' % event_type handler = self.get_handler(event_type) result = handler.capture(self, data=data, **kwargs) if self._filter_exception_type(result): return # data (explicit) culprit takes over auto event detection culprit = result.pop('culprit', None) if data.get('culprit'): culprit = data['culprit'] for k, v in six.iteritems(result): if k not in data: data[k] = v log = data.get('log', {}) if stack and 'stacktrace' not in log: if stack is True: frames = iter_stack_frames() else: frames = stack frames = varmap( lambda k, v: shorten(v, string_length=self.string_max_length, list_length=self.list_max_length), stacks.get_stack_info(frames)) log['stacktrace'] = frames if 'stacktrace' in log and not culprit: culprit = get_culprit(log['stacktrace'], self.include_paths, self.exclude_paths) if 'level' in log and isinstance(log['level'], six.integer_types): log['level'] = logging.getLevelName(log['level']).lower() if log: data['log'] = log if culprit: data['culprit'] = culprit context['custom'] = extra # Run the data through processors for processor in self.processors: data = processor(self, data) # Make sure all data is coerced data = transform(data) data.update({ 'timestamp': date.strftime(defaults.TIMESTAMP_FORMAT), }) return self.build_msg({'errors': [data]})
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)
def _emit(self, record, **kwargs): data = {} for k, v in compat.iteritems(record.__dict__): if "." not in k and k not in ("culprit",): continue data[k] = v stack = getattr(record, "stack", None) if stack is True: stack = iter_stack_frames(config=self.client.config) if stack: frames = [] started = False last_mod = "" for item in stack: if isinstance(item, (list, tuple)): frame, lineno = item else: frame, lineno = item, item.f_lineno if not started: f_globals = getattr(frame, "f_globals", {}) module_name = f_globals.get("__name__", "") if last_mod.startswith("logging") and not module_name.startswith("logging"): started = True else: last_mod = module_name continue frames.append((frame, lineno)) stack = frames custom = getattr(record, "data", {}) # Add in all of the data from the record that we aren't already capturing for k in record.__dict__.keys(): if k in ( "stack", "name", "args", "msg", "levelno", "exc_text", "exc_info", "data", "created", "levelname", "msecs", "relativeCreated", ): continue if k.startswith("_"): continue custom[k] = record.__dict__[k] # If there's no exception being processed, # exc_info may be a 3-tuple of None # http://docs.python.org/library/sys.html#sys.exc_info if record.exc_info and all(record.exc_info): handler = self.client.get_handler("elasticapm.events.Exception") exception = handler.capture(self.client, exc_info=record.exc_info) else: exception = None return self.client.capture( "Message", param_message={"message": compat.text_type(record.msg), "params": record.args}, stack=stack, custom=custom, exception=exception, level=record.levelno, logger_name=record.name, **kwargs )
def _emit(self, record, **kwargs): data = {} for k, v in compat.iteritems(record.__dict__): if '.' not in k and k not in ('culprit', ): continue data[k] = v stack = getattr(record, 'stack', None) if stack is True: stack = iter_stack_frames() if stack: frames = [] started = False last_mod = '' for item in stack: if isinstance(item, (list, tuple)): frame, lineno = item else: frame, lineno = item, item.f_lineno if not started: f_globals = getattr(frame, 'f_globals', {}) module_name = f_globals.get('__name__', '') if last_mod.startswith( 'logging' ) and not module_name.startswith('logging'): started = True else: last_mod = module_name continue frames.append((frame, lineno)) stack = frames custom = getattr(record, 'data', {}) # Add in all of the data from the record that we aren't already capturing for k in record.__dict__.keys(): if k in ('stack', 'name', 'args', 'msg', 'levelno', 'exc_text', 'exc_info', 'data', 'created', 'levelname', 'msecs', 'relativeCreated'): continue if k.startswith('_'): continue custom[k] = record.__dict__[k] date = datetime.datetime.utcfromtimestamp(record.created) # If there's no exception being processed, # exc_info may be a 3-tuple of None # http://docs.python.org/library/sys.html#sys.exc_info if record.exc_info and all(record.exc_info): handler = self.client.get_handler('elasticapm.events.Exception') exception = handler.capture(self.client, exc_info=record.exc_info) else: exception = None return self.client.capture('Message', param_message={ 'message': record.msg, 'params': record.args }, stack=stack, custom=custom, exception=exception, date=date, level=record.levelno, logger_name=record.name, **kwargs)
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
def bench_iter_stack_frames(): for frame, lineno in iter_stack_frames(): info = get_frame_info(frame, lineno)
def _build_msg_for_logging(self, event_type, date=None, context=None, custom=None, stack=None, handled=True, **kwargs): """ Captures, processes and serializes an event into a dict object """ transaction = get_transaction() if transaction: transaction_context = deepcopy(transaction.context) else: transaction_context = {} event_data = {} if custom is None: custom = {} if date is not None: warnings.warn( "The date argument is no longer evaluated and will be removed in a future release", DeprecationWarning) date = time.time() if stack is None: stack = self.config.auto_log_stacks if context: transaction_context.update(context) context = transaction_context else: context = transaction_context event_data["context"] = context if transaction and transaction.tags: context["tags"] = deepcopy(transaction.tags) # if '.' not in event_type: # Assume it's a builtin event_type = "elasticapm.events.%s" % event_type handler = self.get_handler(event_type) result = handler.capture(self, **kwargs) if self._filter_exception_type(result): return # data (explicit) culprit takes over auto event detection culprit = result.pop("culprit", None) if custom.get("culprit"): culprit = custom.pop("culprit") for k, v in compat.iteritems(result): if k not in event_data: event_data[k] = v log = event_data.get("log", {}) if stack and "stacktrace" not in log: if stack is True: frames = stacks.iter_stack_frames(skip=3) else: frames = stack frames = stacks.get_stack_info( frames, with_locals=self.config.collect_local_variables in ("errors", "all"), library_frame_context_lines=self.config. source_lines_error_library_frames, in_app_frame_context_lines=self.config. source_lines_error_app_frames, include_paths_re=self.include_paths_re, exclude_paths_re=self.exclude_paths_re, 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, ), ) log["stacktrace"] = frames if "stacktrace" in log and not culprit: culprit = stacks.get_culprit(log["stacktrace"], self.config.include_paths, self.config.exclude_paths) if "level" in log and isinstance(log["level"], compat.integer_types): log["level"] = logging.getLevelName(log["level"]).lower() if log: event_data["log"] = log if culprit: event_data["culprit"] = culprit if "custom" in context: context["custom"].update(custom) else: context["custom"] = custom # Make sure all data is coerced event_data = transform(event_data) if "exception" in event_data: event_data["exception"]["handled"] = bool(handled) event_data["timestamp"] = int(date * 1000000) transaction = get_transaction() if transaction: if transaction.trace_parent: event_data["trace_id"] = transaction.trace_parent.trace_id event_data["parent_id"] = transaction.id event_data["transaction_id"] = transaction.id return event_data
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)