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 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(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 get_me_a_filtered_frame(hide=True): __traceback_hide__ = True if not hide: del __traceback_hide__ return list(stacks.iter_stack_frames())
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 __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("zuqa.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 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, "zuqa_labels"): self.logger.debug( "Inserting zuqa log_record_factory into logging") # Late import due to circular imports import zuqa.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": "zuqa-python/%s" % zuqa.VERSION, } 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, "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 = ("zuqa.", "_functools") else: skip_modules = ("zuqa.", ) 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( "zuqa.metrics.sets.breakdown.BreakdownMetricSet") 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 config.enabled: self.start_threads()
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 = execution_context.get_transaction() span = execution_context.get_span() 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.labels: context["tags"] = deepcopy(transaction.labels) # if '.' not in event_type: # Assume it's a builtin event_type = "zuqa.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, config=self.config) 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, dict_length=self.config.local_var_dict_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) if transaction: if transaction.trace_parent: event_data["trace_id"] = transaction.trace_parent.trace_id # parent id might already be set in the handler event_data.setdefault("parent_id", span.id if span else transaction.id) event_data["transaction_id"] = transaction.id event_data["transaction"] = { "sampled": transaction.is_sampled, "type": transaction.transaction_type } return event_data
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("zuqa.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 )